diff options
author | Francesco Giudici <fgiudici@redhat.com> | 2017-12-08 00:53:46 +0100 |
---|---|---|
committer | Francesco Giudici <fgiudici@redhat.com> | 2017-12-08 00:53:46 +0100 |
commit | 4d1796b2c7caf67805704218e554fc39ceb94614 (patch) | |
tree | baf5a87f745cb5b416277de49f7d733bf5595c1a | |
parent | a9b507932465888504cbf93bcf3a86b1c3b1df79 (diff) | |
parent | 9d5cd7eae8edc8c558d26f04ffd163effafe57f9 (diff) | |
download | NetworkManager-4d1796b2c7caf67805704218e554fc39ceb94614.tar.gz |
merge: branch 'fg/team_abstraction_tests_and_fixes_rh1398925'
https://bugzilla.redhat.com/show_bug.cgi?id=1398925
29 files changed, 3701 insertions, 1249 deletions
diff --git a/.gitignore b/.gitignore index c68d11f516..05010072d1 100644 --- a/.gitignore +++ b/.gitignore @@ -155,9 +155,7 @@ test-*.trs /libnm-core/tests/test-keyfile /libnm-core/tests/test-need-secrets /libnm-core/tests/test-secrets -/libnm-core/tests/test-setting-8021x -/libnm-core/tests/test-setting-bond -/libnm-core/tests/test-setting-dcb +/libnm-core/tests/test-setting /libnm-core/nm-dbus-types.xml /libnm-core/nm-vpn-dbus-types.xml diff --git a/Makefile.am b/Makefile.am index 5da6aa95df..26e4448160 100644 --- a/Makefile.am +++ b/Makefile.am @@ -638,9 +638,7 @@ check_programs += \ libnm-core/tests/test-general \ libnm-core/tests/test-keyfile \ libnm-core/tests/test-secrets \ - libnm-core/tests/test-setting-8021x \ - libnm-core/tests/test-setting-bond \ - libnm-core/tests/test-setting-dcb \ + libnm-core/tests/test-setting \ libnm-core/tests/test-settings-defaults GLIB_GENERATED += \ @@ -664,9 +662,7 @@ libnm_core_tests_test_crypto_CPPFLAGS = $(libnm_core_tests_cppflags) libnm_core_tests_test_general_CPPFLAGS = $(libnm_core_tests_cppflags) libnm_core_tests_test_keyfile_CPPFLAGS = $(libnm_core_tests_cppflags) libnm_core_tests_test_secrets_CPPFLAGS = $(libnm_core_tests_cppflags) -libnm_core_tests_test_setting_8021x_CPPFLAGS = $(libnm_core_tests_cppflags) -libnm_core_tests_test_setting_bond_CPPFLAGS = $(libnm_core_tests_cppflags) -libnm_core_tests_test_setting_dcb_CPPFLAGS = $(libnm_core_tests_cppflags) +libnm_core_tests_test_setting_CPPFLAGS = $(libnm_core_tests_cppflags) libnm_core_tests_test_settings_defaults_CPPFLAGS = $(libnm_core_tests_cppflags) libnm_core_tests_test_general_SOURCES = \ @@ -686,9 +682,7 @@ libnm_core_tests_test_crypto_LDADD = $(libnm_core_tests_ldadd) libnm_core_tests_test_general_LDADD = $(libnm_core_tests_ldadd) libnm_core_tests_test_keyfile_LDADD = $(libnm_core_tests_ldadd) libnm_core_tests_test_secrets_LDADD = $(libnm_core_tests_ldadd) -libnm_core_tests_test_setting_8021x_LDADD = $(libnm_core_tests_ldadd) -libnm_core_tests_test_setting_bond_LDADD = $(libnm_core_tests_ldadd) -libnm_core_tests_test_setting_dcb_LDADD = $(libnm_core_tests_ldadd) +libnm_core_tests_test_setting_LDADD = $(libnm_core_tests_ldadd) libnm_core_tests_test_settings_defaults_LDADD = $(libnm_core_tests_ldadd) $(libnm_core_tests_test_compare_OBJECTS): $(libnm_core_lib_h_pub_mkenums) @@ -696,9 +690,7 @@ $(libnm_core_tests_test_crypto_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(libnm_core_tests_test_general_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(libnm_core_tests_test_keyfile_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(libnm_core_tests_test_secrets_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -$(libnm_core_tests_test_setting_8021x_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -$(libnm_core_tests_test_setting_bond_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -$(libnm_core_tests_test_setting_dcb_OBJECTS): $(libnm_core_lib_h_pub_mkenums) +$(libnm_core_tests_test_setting_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(libnm_core_tests_test_settings_defaults_OBJECTS): $(libnm_core_lib_h_pub_mkenums) # test-cert.p12 created with: @@ -2761,7 +2753,7 @@ endif # src/devices/team ############################################################################### -if WITH_TEAMDCTL +if WITH_TEAM core_plugins += src/devices/team/libnm-device-plugin-team.la diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 706c1123ba..75508d7db9 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -240,6 +240,150 @@ _parse_ip_route (int family, return route; } +static char * +_dump_team_link_watcher (NMTeamLinkWatcher *watcher) +{ + const char *name; + NMTeamLinkWatcherArpPingFlags flags; + GString *w_dump; + + if (!watcher) + return NULL; + + w_dump = g_string_new (NULL); + name = nm_team_link_watcher_get_name (watcher); + g_string_append_printf (w_dump, "name=%s", name); + +#define DUMP_WATCHER_INT(str, watcher, name, key) \ + G_STMT_START { \ + int _val = nm_team_link_watcher_get_##key (watcher); \ + \ + if (_val) \ + g_string_append_printf (str, " %s=%d", name, _val); \ + } G_STMT_END; + + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { + DUMP_WATCHER_INT (w_dump, watcher, "delay-up", delay_up); + DUMP_WATCHER_INT (w_dump, watcher, "delay-down", delay_down); + return g_string_free (w_dump, FALSE); + } + /* NM_TEAM_LINK_WATCHER_NSNA_PING and NM_TEAM_LINK_WATCHER_ARP_PING */ + DUMP_WATCHER_INT (w_dump, watcher, "init-wait", init_wait); + DUMP_WATCHER_INT (w_dump, watcher, "initerval", interval); + DUMP_WATCHER_INT (w_dump, watcher, "missed-max", missed_max); +#undef DUMP_WATCHER_INT + g_string_append_printf (w_dump, " target-host=%s", + nm_team_link_watcher_get_target_host (watcher)); + + if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) + return g_string_free (w_dump, FALSE); + + g_string_append_printf (w_dump, " source-host=%s", + nm_team_link_watcher_get_source_host (watcher)); + flags = nm_team_link_watcher_get_flags (watcher); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE) + g_string_append_printf (w_dump, " validate-active=true"); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE) + g_string_append_printf (w_dump, " validate-inactive=true"); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS) + g_string_append_printf (w_dump, "send-always=true"); + + return g_string_free (w_dump, FALSE); +} + +static NMTeamLinkWatcher * +_parse_team_link_watcher (const char *str, + GError **error) +{ + gs_strfreev char **watcherv = NULL; + gs_free char *str_clean = NULL; + guint i; + gs_free const char *name = NULL; + int val1 = 0, val2 = 0, val3 = 3; + gs_free const char *target_host = NULL; + gs_free const char *source_host = NULL; + NMTeamLinkWatcherArpPingFlags flags = 0; + + nm_assert (str); + nm_assert (!error || !*error); + + str_clean = g_strstrip (g_strdup (str)); + watcherv = nmc_strsplit_set (str_clean, " \t", 0); + if (!watcherv || !watcherv[0]) { + g_set_error (error, 1, 0, "'%s' is not valid", str); + return NULL; + } + + for (i = 0; watcherv[i]; i++) { + gs_strfreev char **pair = NULL; + + pair = nmc_strsplit_set (watcherv[i], "=", 0); + if (!pair[0]) { + g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], + "properties should be specified as 'key=value'"); + return NULL; + } + if (!pair[1]) { + g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], + "missing key value"); + return NULL; + } + if (pair[2]) { + g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], + "properties should be specified as 'key=value'"); + return NULL; + } + + if (nm_streq (pair[0], "name")) + name = g_strdup (pair[1]); + else if ( nm_streq (pair[0], "delay-up") + || nm_streq (pair[0], "init-wait")) + val1 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1); + else if ( nm_streq (pair[0], "delay-down") + || nm_streq (pair[0], "interval")) + val2 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1); + else if (nm_streq (pair[0], "missed-max")) + val3 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1); + else if (nm_streq (pair[0], "target-host")) + target_host = g_strdup (pair[1]); + else if (nm_streq (pair[0], "source-host")) + source_host = g_strdup (pair[1]); + else if (nm_streq (pair[0], "validate-active")) { + if (nm_streq (pair[1], "true")) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; + } else if (nm_streq (pair[0], "validate-inactive")) { + if (nm_streq (pair[1], "true")) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE; + } else if (nm_streq (pair[0], "send-always")) { + if (nm_streq (pair[1], "true")) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; + } else { + g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], + "unknown key"); + return NULL; + } + + if ((val1 < 0) || (val2 < 0) || (val3 < 0)) { + g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], + "value is not a valid number [0, MAXINT]"); + return NULL; + } + } + + if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) + return nm_team_link_watcher_new_ethtool (val1, val2, error); + else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) + return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, error); + else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING)) + return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host, source_host, flags, error); + + if (!name) + g_set_error (error, 1, 0, "link watcher name missing"); + else + g_set_error (error, 1, 0, "unknown link watcher name: '%s'", name); + return NULL; +} + /* Max priority values from libnm-core/nm-setting-vlan.c */ #define MAX_SKB_PRIO G_MAXUINT32 #define MAX_8021P_PRIO 7 /* Max 802.1p priority */ @@ -3707,6 +3851,150 @@ DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_runner_tx_hash, _validate_and_remove_team_runner_tx_hash) static gconstpointer +_get_fcn_team_link_watchers (ARGS_GET_FCN) +{ + NMSettingTeam *s_team = NM_SETTING_TEAM (setting); + GString *printable; + guint32 num_watchers, i; + NMTeamLinkWatcher *watcher; + char *watcher_str; + + RETURN_UNSUPPORTED_GET_TYPE (); + + printable = g_string_new (NULL); + + num_watchers = nm_setting_team_get_num_link_watchers (s_team); + for (i = 0; i < num_watchers; i++) { + watcher = nm_setting_team_get_link_watcher (s_team, i); + watcher_str = _dump_team_link_watcher (watcher); + if (watcher_str) { + if (printable->len > 0) + g_string_append (printable, ", "); + g_string_append (printable, watcher_str); + g_free (watcher_str); + } + } + + RETURN_STR_TO_FREE (g_string_free (printable, FALSE)); +} + +static gboolean +_set_fcn_team_link_watchers (ARGS_SET_FCN) +{ + gs_strfreev char **strv = NULL; + const char *const*iter; + NMTeamLinkWatcher *watcher; + + strv = nmc_strsplit_set (value, ",", 0); + for (iter = (const char *const*) strv; *iter; iter++) { + watcher = _parse_team_link_watcher (*iter, error); + if (!watcher) + return FALSE; + nm_setting_team_add_link_watcher (NM_SETTING_TEAM (setting), watcher); + nm_team_link_watcher_unref (watcher); + } + return TRUE; +} + +static gboolean +_validate_and_remove_team_link_watcher (NMSettingTeam *setting, + const char *watcher_str, + GError **error) +{ + NMTeamLinkWatcher *watcher; + gboolean ret; + + watcher = _parse_team_link_watcher (watcher_str, error); + if (!watcher) + return FALSE; + + ret = nm_setting_team_remove_link_watcher_by_value (setting, watcher); + if (!ret) { + g_set_error (error, 1, 0, _("the property doesn't contain link watcher '%s'"), + watcher_str); + } + nm_team_link_watcher_unref (watcher); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_link_watchers, + NM_SETTING_TEAM, + nm_setting_team_get_num_link_watchers, + nm_setting_team_remove_link_watcher, + _validate_and_remove_team_link_watcher) + +static gconstpointer +_get_fcn_team_port_link_watchers (ARGS_GET_FCN) +{ + NMSettingTeamPort *s_team_port = NM_SETTING_TEAM_PORT (setting); + GString *printable; + guint32 num_watchers, i; + NMTeamLinkWatcher *watcher; + char *watcher_str; + + RETURN_UNSUPPORTED_GET_TYPE (); + + printable = g_string_new (NULL); + + num_watchers = nm_setting_team_port_get_num_link_watchers (s_team_port); + for (i = 0; i < num_watchers; i++) { + watcher = nm_setting_team_port_get_link_watcher (s_team_port, i); + watcher_str = _dump_team_link_watcher (watcher); + if (watcher_str) { + if (printable->len > 0) + g_string_append (printable, ", "); + g_string_append (printable, watcher_str); + g_free (watcher_str); + } + } + + RETURN_STR_TO_FREE (g_string_free (printable, FALSE)); +} + +static gboolean +_set_fcn_team_port_link_watchers (ARGS_SET_FCN) +{ + gs_strfreev char **strv = NULL; + const char *const*iter; + NMTeamLinkWatcher *watcher; + + strv = nmc_strsplit_set (value, ",", 0); + for (iter = (const char *const*) strv; *iter; iter++) { + watcher = _parse_team_link_watcher (*iter, error); + if (!watcher) + return FALSE; + nm_setting_team_port_add_link_watcher (NM_SETTING_TEAM_PORT (setting), watcher); + nm_team_link_watcher_unref (watcher); + } + return TRUE; +} + +static gboolean +_validate_and_remove_team_port_link_watcher (NMSettingTeamPort *setting, + const char *watcher_str, + GError **error) +{ + NMTeamLinkWatcher *watcher; + gboolean ret; + + watcher = _parse_team_link_watcher (watcher_str, error); + if (!watcher) + return FALSE; + + ret = nm_setting_team_port_remove_link_watcher_by_value (setting, watcher); + if (!ret) { + g_set_error (error, 1, 0, _("the property doesn't contain link watcher '%s'"), + watcher_str); + } + nm_team_link_watcher_unref (watcher); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_port_link_watchers, + NM_SETTING_TEAM_PORT, + nm_setting_team_port_get_num_link_watchers, + nm_setting_team_port_remove_link_watcher, + _validate_and_remove_team_port_link_watcher) + +static gconstpointer _get_fcn_vlan_flags (ARGS_GET_FCN) { NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); @@ -4504,6 +4792,24 @@ static const NMMetaPropertyType _pt_gobject_devices = { "{ \"device\": \"team0\", \"runner\": {\"name\": \"roundrobin\"}, \"ports\": {\"eth1\": {}, \"eth2\": {}} }\n" \ " set team.config /etc/my-team.conf\n") +#define TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE \ + N_("Enter a list of link watchers formatted as dictionaries where the keys " \ + "are teamd properties. Dictionary pairs are in the form: key=value and pairs " \ + "are separated by ' '. Dictionaries are separated with ','.\n" \ + "The keys allowed/required in the dictionary change on the basis of the link " \ + "watcher type, while the only property common to all the link watchers is " \ + " 'name'*, which defines the link watcher to be specified.\n\n" \ + "Properties available for the 'ethtool' link watcher:\n" \ + " 'delay-up', 'delay-down'\n\n" \ + "Properties available for the 'nsna_ping' link watcher:\n" \ + " 'init-wait', 'interval', 'missed-max', 'target-host'*\n\n" \ + "Properties available for the 'arp_ping' include all the ones for 'nsna_ping' and:\n" \ + " 'source-host', 'validate-active', 'validate-inactive', 'send-always'.\n\n" \ + "Properties flagged with a '*' are mandatory.\n\n" \ + "Example:\n" \ + " name=arp_ping,source-host=172.16.1.1,target-host=172.16.1.254; name=ethtool,delay-up=3\n") + + #define DEFINE_DCB_PROPRITY_PROPERTY_TYPE \ .property_type = &_pt_gobject_int, \ .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \ @@ -6188,6 +6494,14 @@ static const NMMetaPropertyInfo *const property_infos_TEAM[] = { NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_PORT_CONFIG), ), ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_LINK_WATCHERS, + .describe_message = TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE, + .property_type = DEFINE_PROPERTY_TYPE ( + .get_fcn = _get_fcn_team_link_watchers, + .set_fcn = _set_fcn_team_link_watchers, + .remove_fcn = _remove_fcn_team_link_watchers, + ), + ), NULL }; @@ -6251,6 +6565,14 @@ static const NMMetaPropertyInfo *const property_infos_TEAM_PORT[] = { ), ), ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LINK_WATCHERS, + .describe_message = TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE, + .property_type = DEFINE_PROPERTY_TYPE ( + .get_fcn = _get_fcn_team_port_link_watchers, + .set_fcn = _set_fcn_team_port_link_watchers, + .remove_fcn = _remove_fcn_team_port_link_watchers, + ), + ), NULL }; diff --git a/clients/common/settings-docs.c.in b/clients/common/settings-docs.c.in index 79cbb715b1..bf57a89b3e 100644 --- a/clients/common/settings-docs.c.in +++ b/clients/common/settings-docs.c.in @@ -316,6 +316,7 @@ #define DESCRIBE_DOC_NM_SETTING_SERIAL_SEND_DELAY N_("Time to delay between each byte sent to the modem, in microseconds.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_STOPBITS N_("Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in \"8n1\" for example.") #define DESCRIBE_DOC_NM_SETTING_TEAM_CONFIG N_("The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.") +#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.") #define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_COUNT N_("Corresponds to the teamd mcast_rejoin.count.") #define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL N_("Corresponds to the teamd mcast_rejoin.interval.") #define DESCRIBE_DOC_NM_SETTING_TEAM_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") @@ -334,6 +335,7 @@ #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_CONFIG N_("The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.") #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_KEY N_("Corresponds to the teamd ports.PORTIFNAME.lacp_key.") #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.lacp_prio.") +#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.") #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.prio.") #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_QUEUE_ID N_("Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config.") diff --git a/configure.ac b/configure.ac index c358d48821..8a849d3f0a 100644 --- a/configure.ac +++ b/configure.ac @@ -657,10 +657,10 @@ else have_team_prereq=no fi -AC_ARG_ENABLE(teamdctl, - AS_HELP_STRING([--enable-teamdctl], [enable Teamd control support]), - [enable_teamdctl=${enableval}], [enable_teamdctl=${have_team_prereq}]) -if (test "${enable_teamdctl}" = "yes"); then +AC_ARG_ENABLE(team, + AS_HELP_STRING([--enable-team], [enable Teamd control support]), + [enable_team=${enableval}], [enable_team=${have_team_prereq}]) +if (test "${enable_team}" = "yes"); then if test "$have_teamdctl" = "no"; then AC_MSG_ERROR(Libteamdctl is required for team support) fi @@ -669,25 +669,11 @@ if (test "${enable_teamdctl}" = "yes"); then fi # temporary bug workaround LIBTEAMDCTL_CFLAGS=`echo $LIBTEAMDCTL_CFLAGS | sed -e 's:/teamdctl.h::'` - AC_DEFINE(WITH_TEAMDCTL, 1, [Define if you have Teamd control support]) + AC_DEFINE(WITH_TEAM, 1, [Define if you have Teamd control support]) else - AC_DEFINE(WITH_TEAMDCTL, 0, [Define if you have Teamd control support]) -fi -AM_CONDITIONAL(WITH_TEAMDCTL, test "${enable_teamdctl}" = "yes") - -# Jansson for team configuration validation -AC_ARG_ENABLE(json-validation, - AS_HELP_STRING([--enable-json-validation], [Enable JSON validation in libnm]), - [enable_json_validation=${enableval}], - [enable_json_validation=${have_jansson}]) -if (test "${enable_json_validation}" == "no"); then - AC_DEFINE(WITH_JSON_VALIDATION, 0, [Define if JSON validation in libnm is enabled]) -else - if test "$have_jansson" = "no"; then - AC_MSG_ERROR([jansson is needed for team configuration validation. Use --disable-json-validation to build without it.]) - fi - AC_DEFINE(WITH_JSON_VALIDATION, 1, [Define if JSON validation in libnm is enabled]) + AC_DEFINE(WITH_TEAM, 0, [Define if you have Teamd control support]) fi +AM_CONDITIONAL(WITH_TEAM, test "${enable_team}" = "yes") # we usually compile with polkit support. --enable-polkit=yes|no only sets the # default configuration for main.auth-polkit. User can always enable/disable polkit @@ -1411,7 +1397,7 @@ echo " ppp: $enable_ppp ${PPPD_PLUGIN_DIR}" echo " modemmanager-1: $with_modem_manager_1" echo " ofono: $with_ofono" echo " concheck: $enable_concheck" -echo " libteamdctl: $enable_teamdctl" +echo " team: $enable_team" echo " ovs: $enable_ovs" echo " libnm-glib: $with_libnm_glib" echo " nmcli: $build_nmcli" @@ -1450,7 +1436,6 @@ echo " valgrind: $with_valgrind $with_valgrind_suppressions" echo " code coverage: $enable_code_coverage" echo " LTO: $enable_lto" echo " linker garbage collection: $enable_ld_gc" -echo " JSON validation for libnm: $enable_json_validation" echo " sanitizers: $sanitizers" echo " Mozilla Public Suffix List: $with_libpsl" echo diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index e0a0849933..b49b1cf07f 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -434,9 +434,9 @@ intltoolize --automake --copy --force --disable-gtk-doc \ %endif %if %{with team} - --enable-teamdctl=yes \ + --enable-team=yes \ %else - --enable-teamdctl=no \ + --enable-team=no \ %endif %if %{with ovs} --enable-ovs=yes \ @@ -472,8 +472,7 @@ intltoolize --automake --copy --force --with-dist-version=%{version}-%{release} \ --with-config-plugins-default='ifcfg-rh,ibft' \ --with-config-dns-rc-manager-default=symlink \ - --with-config-logging-backend-default=journal \ - --enable-json-validation + --with-config-logging-backend-default=journal make %{?_smp_mflags} diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index 30d519c40a..236dad8027 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -501,6 +501,15 @@ cert_writer (KeyfileWriterInfo *info, cert_writer_default (info->connection, info->keyfile, &type_data); } +static void +null_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + /* skip */ +} + /*****************************************************************************/ typedef struct { @@ -576,6 +585,69 @@ static KeyWriter key_writers[] = { { NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, cert_writer }, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_MCAST_REJOIN_COUNT, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_TX_HASH, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_TX_BALANCER, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_ACTIVE, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_FAST_RATE, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_SYS_PRIO, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_MIN_PORTS, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_LINK_WATCHERS, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_QUEUE_ID, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_PRIO, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_STICKY, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_LACP_PRIO, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_LACP_KEY, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_LINK_WATCHERS, + null_writer}, { NULL, NULL, NULL } }; diff --git a/libnm-core/nm-property-compare.c b/libnm-core/nm-property-compare.c index d13d019f0a..64ed663c22 100644 --- a/libnm-core/nm-property-compare.c +++ b/libnm-core/nm-property-compare.c @@ -56,6 +56,38 @@ _nm_property_compare_collection (GVariant *value1, GVariant *value2) } static gint +_nm_property_compare_vardict (GVariant *value1, GVariant *value2) +{ + GVariantIter iter; + int len1, len2; + const char *key; + GVariant *val1, *val2; + + len1 = g_variant_n_children (value1); + len2 = g_variant_n_children (value2); + + if (len1 != len2) + return len1 < len2 ? -1 : 1; + + g_variant_iter_init (&iter, value1); + while (g_variant_iter_next (&iter, "{&sv}", &key, &val1)) { + if (!g_variant_lookup (value2, key, "v", &val2)) { + g_variant_unref (val1); + return -1; + } + if (!g_variant_equal (val1, val2)) { + g_variant_unref (val1); + g_variant_unref (val2); + return -1; + } + g_variant_unref (val1); + g_variant_unref (val2); + } + + return 0; +} + +static gint _nm_property_compare_strdict (GVariant *value1, GVariant *value2) { GVariantIter iter; @@ -106,6 +138,8 @@ nm_property_compare (GVariant *value1, GVariant *value2) ret = g_variant_compare (value1, value2); else if (g_variant_is_of_type (value1, G_VARIANT_TYPE ("a{ss}"))) ret = _nm_property_compare_strdict (value1, value2); + else if (g_variant_is_of_type (value1, G_VARIANT_TYPE ("a{sv}"))) + ret = _nm_property_compare_vardict (value1, value2); else if (g_variant_type_is_array (type1)) ret = _nm_property_compare_collection (value1, value2); else if (g_variant_type_is_tuple (type1)) diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c index 87a96ec66b..3471fe58f2 100644 --- a/libnm-core/nm-setting-team-port.c +++ b/libnm-core/nm-setting-team-port.c @@ -15,6 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * + * Copyright 2017 Red Hat, Inc. * Copyright 2013 Jiri Pirko <jiri@resnulli.us> */ @@ -29,7 +30,6 @@ #include "nm-utils-private.h" #include "nm-connection-private.h" #include "nm-setting-connection.h" -#include "nm-setting-team.h" /** * SECTION:nm-setting-team-port @@ -52,6 +52,7 @@ typedef struct { gboolean sticky; int lacp_prio; int lacp_key; + GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */ } NMSettingTeamPortPrivate; /* Keep aligned with _prop_to_keys[] */ @@ -63,18 +64,20 @@ enum { PROP_STICKY, PROP_LACP_PRIO, PROP_LACP_KEY, + PROP_LINK_WATCHERS, LAST_PROP }; /* Keep aligned with team-port properties enum */ static const _NMUtilsTeamPropertyKeys _prop_to_keys[LAST_PROP] = { - [PROP_0] = { NULL, NULL, NULL, 0 }, - [PROP_CONFIG] = { NULL, NULL, NULL, 0 }, - [PROP_QUEUE_ID] = { "queue_id", NULL, NULL, NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT }, - [PROP_PRIO] = { "prio", NULL, NULL, 0 }, - [PROP_STICKY] = { "sticky", NULL, NULL, 0 }, - [PROP_LACP_PRIO] = { "lacp_prio", NULL, NULL, NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT }, - [PROP_LACP_KEY] = { "lacp_key", NULL, NULL, 0 }, + [PROP_0] = { NULL, NULL, NULL, 0 }, + [PROP_CONFIG] = { NULL, NULL, NULL, 0 }, + [PROP_QUEUE_ID] = { "queue_id", NULL, NULL, NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT }, + [PROP_PRIO] = { "prio", NULL, NULL, 0 }, + [PROP_STICKY] = { "sticky", NULL, NULL, 0 }, + [PROP_LACP_PRIO] = { "lacp_prio", NULL, NULL, NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT }, + [PROP_LACP_KEY] = { "lacp_key", NULL, NULL, 0 }, + [PROP_LINK_WATCHERS] = { "link_watch", NULL, NULL, 0 } }; @@ -185,6 +188,159 @@ nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting) return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->lacp_key; } +/** + * nm_setting_team_port_get_num_link_watchers: + * @setting: the #NMSettingTeamPort + * + * Returns: the number of configured link watchers + * + * Since: 1.12 + **/ +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; +} + +/** + * nm_setting_team_port_get_link_watcher: + * @setting: the #NMSettingTeamPort + * @idx: index number of the link watcher to return + * + * Returns: (transfer none): the link watcher at index @idx. + * + * Since: 1.12 + **/ +NMTeamLinkWatcher * +nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + 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]; +} + +/** + * nm_setting_team_port_add_link_watcher: + * @setting: the #NMSettingTeamPort + * @link_watcher: the link watcher to add + * + * Appends a new link watcher to the setting. + * + * Returns: %TRUE if the link watcher is added; %FALSE if an identical link + * watcher was already there. + * + * Since: 1.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_dup (link_watcher)); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return TRUE; +} + +/** + * nm_setting_team_port_remove_link_watcher: + * @setting: the #NMSettingTeamPort + * @idx: index number of the link watcher to remove + * + * Removes the link watcher at index #idx. + * + * Since: 1.12 + **/ +void +nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + 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); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); +} + +/** + * nm_setting_team_port_remove_link_watcher_by_value: + * @setting: the #NMSettingTeamPort + * @link_watcher: the link watcher to remove + * + * Removes the link watcher entry matching link_watcher. + * + * Returns: %TRUE if the link watcher was found and removed, %FALSE otherwise. + * + * Since: 1.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); + + 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); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_team_port_clear_link_watchers: + * @setting: the #NMSettingTeamPort + * + * Removes all configured link watchers. + * + * Since: 1.12 + **/ +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)); + + g_ptr_array_set_size (priv->link_watchers, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); +} + +static GVariant * +team_link_watchers_to_dbus (const GValue *prop_value) +{ + return _nm_utils_team_link_watchers_to_variant (g_value_get_boxed (prop_value)); +} + +static void +team_link_watchers_from_dbus (GVariant *dbus_value, + GValue *prop_value) +{ + g_value_take_boxed (prop_value, _nm_utils_team_link_watchers_from_variant (dbus_value)); +} + static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { @@ -253,6 +409,8 @@ compare_property (NMSetting *setting, NMSettingCompareFlags flags) { NMSettingClass *parent_class; + NMSettingTeamPortPrivate *a_priv, *b_priv; + guint i, j; /* If we are trying to match a connection in order to assume it (and thus * @flags contains INFERRABLE), use the "relaxed" matching for team @@ -265,6 +423,24 @@ compare_property (NMSetting *setting, NM_SETTING_TEAM_PORT_GET_PRIVATE (other)->config, TRUE); } + if (nm_streq0 (prop_spec->name, NM_SETTING_TEAM_PORT_LINK_WATCHERS)) { + a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other); + + if (a_priv->link_watchers->len != b_priv->link_watchers->len) + return FALSE; + for (i = 0; i < a_priv->link_watchers->len; i++) { + for (j = 0; j < b_priv->link_watchers->len; j++) { + if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i], + b_priv->link_watchers->pdata[j])) { + break; + } + } + if (j == b_priv->link_watchers->len) + return FALSE; + } + return TRUE; + } /* Otherwise chain up to parent to handle generic compare */ parent_class = NM_SETTING_CLASS (nm_setting_team_port_parent_class); @@ -278,6 +454,7 @@ nm_setting_team_port_init (NMSettingTeamPort *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); } #define JSON_TO_VAL(typ, id) _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], TRUE) @@ -299,6 +476,9 @@ set_property (GObject *object, guint prop_id, 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)) @@ -341,6 +521,15 @@ set_property (GObject *object, guint prop_id, 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_dup, + (GDestroyNotify) nm_team_link_watcher_unref); + if (priv->link_watchers->len) + align_value = value; + align_config = TRUE; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -375,6 +564,11 @@ get_property (GObject *object, guint prop_id, 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, + (NMUtilsCopyFunc) nm_team_link_watcher_dup, + (GDestroyNotify) nm_team_link_watcher_unref)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -387,6 +581,7 @@ finalize (GObject *object) NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object); g_free (priv->config); + g_ptr_array_unref (priv->link_watchers); G_OBJECT_CLASS (nm_setting_team_port_parent_class)->finalize (object); } @@ -500,5 +695,31 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NMSettingTeamPort:link-watchers: + * + * Link watchers configuration for the connection: each link watcher is + * defined by a dictionary, whose keys depend upon the selected link + * watcher. Available link watchers are 'ethtool', 'nsna_ping' and + * 'arp_ping' and it is specified in the dictionary with the key 'name'. + * Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; + * nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; + * arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', + * 'validate-incative', 'send-always'. See teamd.conf man for more details. + * + * Element-Type: NMTeamLinkWatcher + * Since: 1.12 + **/ + g_object_class_install_property + (object_class, PROP_LINK_WATCHERS, + g_param_spec_boxed (NM_SETTING_TEAM_PORT_LINK_WATCHERS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + _nm_setting_class_transform_property (parent_class, + NM_SETTING_TEAM_PORT_LINK_WATCHERS, + G_VARIANT_TYPE ("aa{sv}"), + team_link_watchers_to_dbus, + team_link_watchers_from_dbus); } diff --git a/libnm-core/nm-setting-team-port.h b/libnm-core/nm-setting-team-port.h index 2643daba3c..4ecb183847 100644 --- a/libnm-core/nm-setting-team-port.h +++ b/libnm-core/nm-setting-team-port.h @@ -26,6 +26,7 @@ #endif #include "nm-setting.h" +#include "nm-setting-team.h" G_BEGIN_DECLS @@ -38,12 +39,13 @@ G_BEGIN_DECLS #define NM_SETTING_TEAM_PORT_SETTING_NAME "team-port" -#define NM_SETTING_TEAM_PORT_CONFIG "config" -#define NM_SETTING_TEAM_PORT_QUEUE_ID "queue-id" -#define NM_SETTING_TEAM_PORT_PRIO "prio" -#define NM_SETTING_TEAM_PORT_STICKY "sticky" -#define NM_SETTING_TEAM_PORT_LACP_PRIO "lacp-prio" -#define NM_SETTING_TEAM_PORT_LACP_KEY "lacp-key" +#define NM_SETTING_TEAM_PORT_CONFIG "config" +#define NM_SETTING_TEAM_PORT_QUEUE_ID "queue-id" +#define NM_SETTING_TEAM_PORT_PRIO "prio" +#define NM_SETTING_TEAM_PORT_STICKY "sticky" +#define NM_SETTING_TEAM_PORT_LACP_PRIO "lacp-prio" +#define NM_SETTING_TEAM_PORT_LACP_KEY "lacp-key" +#define NM_SETTING_TEAM_PORT_LINK_WATCHERS "link-watchers" #define NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT -1 #define NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT 255 @@ -79,7 +81,24 @@ NM_AVAILABLE_IN_1_12 gint nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting); NM_AVAILABLE_IN_1_12 gint nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting); - +NM_AVAILABLE_IN_1_12 +guint nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcher * +nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx); +NM_AVAILABLE_IN_1_12 +gboolean +nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_12 +void +nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx); +NM_AVAILABLE_IN_1_12 +gboolean +nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_12 +void nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting); G_END_DECLS #endif /* __NM_SETTING_TEAM_PORT_H__ */ diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c index 8526eb5096..7c2b8caa06 100644 --- a/libnm-core/nm-setting-team.c +++ b/libnm-core/nm-setting-team.c @@ -15,6 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * + * Copyright 2017 Red Hat, Inc. * Copyright 2013 Jiri Pirko <jiri@resnulli.us> */ @@ -36,6 +37,532 @@ * necessary for team connections. **/ +/***************************************************************************** + * NMTeamLinkWatch + *****************************************************************************/ + +G_DEFINE_BOXED_TYPE (NMTeamLinkWatcher, nm_team_link_watcher, + nm_team_link_watcher_dup, nm_team_link_watcher_unref) + +enum LinkWatcherTypes { + LINK_WATCHER_ETHTOOL = 0, + LINK_WATCHER_NSNA_PING = 1, + LINK_WATCHER_ARP_PING = 2 +}; + +static const char* _link_watcher_name[] = { + [LINK_WATCHER_ETHTOOL] = NM_TEAM_LINK_WATCHER_ETHTOOL, + [LINK_WATCHER_NSNA_PING] = NM_TEAM_LINK_WATCHER_NSNA_PING, + [LINK_WATCHER_ARP_PING] = NM_TEAM_LINK_WATCHER_ARP_PING +}; + +struct NMTeamLinkWatcher { + guint refcount; + + guint8 type; /* LinkWatcherTypes */ + + /* + * The union is constructed in order to allow mapping the options of all the + * watchers on the arp_ping one: this would allow to manipulate all the watchers + * by using the arp_ping struct. See for instance the nm_team_link_watcher_unref() + * and nm_team_link_watcher_equal() functions. So, if you need to change the union + * be careful. + */ + union { + struct { + int delay_up; + int delay_down; + } ethtool; + struct { + int init_wait; + int interval; + int missed_max; + char *target_host; + } nsna_ping; + struct { + int init_wait; + int interval; + int missed_max; + char *target_host; + char *source_host; + NMTeamLinkWatcherArpPingFlags flags; + } arp_ping; + }; +}; + +#define _CHECK_WATCHER_VOID(watcher) \ + G_STMT_START { \ + g_return_if_fail (watcher != NULL); \ + g_return_if_fail (watcher->refcount > 0); \ + g_return_if_fail (watcher->type <= LINK_WATCHER_ARP_PING); \ + } G_STMT_END + +#define _CHECK_WATCHER(watcher, err_val) \ + G_STMT_START { \ + g_return_val_if_fail (watcher != NULL, err_val); \ + g_return_val_if_fail (watcher->refcount > 0, err_val); \ + g_return_val_if_fail (watcher->type <= LINK_WATCHER_ARP_PING, err_val); \ + } G_STMT_END + +/** + * nm_team_link_watcher_new_ethtool: + * @delay_up: delay_up value + * @delay_down: delay_down value + * @error: this call never fails, so this var is not used but kept for format + * consistency with the link_watcher constructors of other type + * + * Creates a new ethtool #NMTeamLinkWatcher object + * + * Returns: (transfer full): the new #NMTeamLinkWatcher object + * + * Since: 1.12 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_new_ethtool (gint delay_up, + gint delay_down, + GError **error) +{ + NMTeamLinkWatcher *watcher; + const char *val_fail = NULL; + + if (delay_up < 0 || delay_up > G_MAXINT32) + val_fail = "delay-up"; + if (delay_down < 0 || delay_down > G_MAXINT32) + val_fail = "delay-down"; + if (val_fail) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("%s is out of range [0, %d]"), val_fail, G_MAXINT32); + return NULL; + } + + watcher = g_slice_new0 (NMTeamLinkWatcher); + watcher->refcount = 1; + + watcher->type = LINK_WATCHER_ETHTOOL; + watcher->ethtool.delay_up = delay_up; + watcher->ethtool.delay_down = delay_down; + + return watcher; +} + +/** + * nm_team_link_watcher_new_nsna_ping: + * @init_wait: init_wait value + * @interval: interval value + * @missed_max: missed_max value + * @target_host: the host name or the ipv6 address that will be used as + * target address in the NS packet + * @error: (out) (allow-none): location to store the error on failure + * + * Creates a new nsna_ping #NMTeamLinkWatcher object + * + * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error + * + * Since: 1.12 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_new_nsna_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + GError **error) +{ + NMTeamLinkWatcher *watcher; + gs_strfreev gchar **strv = NULL; + const char *val_fail = NULL; + + if (!target_host) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("Missing target-host in nsna_ping link watcher")); + return NULL; + } + + strv = g_strsplit_set (target_host, " \\/\t=\"\'", 0); + if (strv[1]) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("target-host '%s' contains invalid characters"), target_host); + return NULL; + } + + if (init_wait < 0 || init_wait > G_MAXINT32) + val_fail = "init-wait"; + if (interval < 0 || interval > G_MAXINT32) + val_fail = "interval"; + if (missed_max < 0 || missed_max > G_MAXINT32) + val_fail = "missed-max"; + if (val_fail) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("%s is out of range [0, %d]"), val_fail, G_MAXINT32); + return NULL; + } + + watcher = g_slice_new0 (NMTeamLinkWatcher); + watcher->refcount = 1; + + watcher->type = LINK_WATCHER_NSNA_PING; + watcher->nsna_ping.init_wait = init_wait; + watcher->nsna_ping.interval = interval; + watcher->nsna_ping.missed_max = missed_max; + watcher->nsna_ping.target_host = g_strdup (target_host); + + return watcher; +} + +/** + * nm_team_link_watcher_new_arp_ping: + * @init_wait: init_wait value + * @interval: interval value + * @missed_max: missed_max value + * @target_host: the host name or the ip address that will be used as destination + * address in the arp request + * @source_host: the host name or the ip address that will be used as source + * address in the arp request + * @flags: the watcher #NMTeamLinkWatcherArpPingFlags + * @error: (out) (allow-none): location to store the error on failure + * + * Creates a new arp_ping #NMTeamLinkWatcher object + * + * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error + * + * Since: 1.12 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_new_arp_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + const char *source_host, + NMTeamLinkWatcherArpPingFlags flags, + GError **error) +{ + NMTeamLinkWatcher *watcher; + gs_strfreev gchar **strv = NULL; + const char *val_fail = NULL; + + if (!target_host || !source_host) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("Missing %s in arp_ping link watcher"), + target_host ? "source-host" : "target-host"); + return NULL; + } + + strv = g_strsplit_set (target_host, " \\/\t=\"\'", 0); + if (strv[1]) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("target-host '%s' contains invalid characters"), target_host); + return NULL; + } + g_strfreev (strv); + + strv = g_strsplit_set (source_host, " \\/\t=\"\'", 0); + if (strv[1]) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("source-host '%s' contains invalid characters"), source_host); + return NULL; + } + + if (init_wait < 0 || init_wait > G_MAXINT32) + val_fail = "init-wait"; + if (interval < 0 || interval > G_MAXINT32) + val_fail = "interval"; + if (missed_max < 0 || missed_max > G_MAXINT32) + val_fail = "missed-max"; + if (val_fail) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("%s is out of range [0, %d]"), val_fail, G_MAXINT32); + return NULL; + } + + watcher = g_slice_new0 (NMTeamLinkWatcher); + watcher->refcount = 1; + + watcher->type = LINK_WATCHER_ARP_PING; + watcher->arp_ping.init_wait = init_wait; + watcher->arp_ping.interval = interval; + watcher->arp_ping.missed_max = missed_max; + watcher->arp_ping.target_host = g_strdup (target_host); + watcher->arp_ping.source_host = g_strdup (source_host); + watcher->arp_ping.flags = flags; + + return watcher; +} + +/** + * nm_team_link_watcher_ref: + * @watcher: the #NMTeamLinkWatcher + * + * Increases the reference count of the object. + * + * Since: 1.12 + **/ +void +nm_team_link_watcher_ref (NMTeamLinkWatcher *watcher){ + _CHECK_WATCHER_VOID (watcher); + + watcher->refcount++; +} + +/** + * nm_team_link_watcher_unref: + * @watcher: the #NMTeamLinkWatcher + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.12 + **/ +void +nm_team_link_watcher_unref (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER_VOID (watcher); + + watcher->refcount--; + if (watcher->refcount == 0) { + g_free (watcher->arp_ping.target_host); + g_free (watcher->arp_ping.source_host); + g_slice_free (NMTeamLinkWatcher, watcher); + } +} + +/** + * nm_team_link_watcher_equal: + * @watcher: the #NMTeamLinkWatcher + * @other: the #NMTeamLinkWatcher to compare @watcher to. + * + * Determines if two #NMTeamLinkWatcher objects contain the same values + * in all the properties. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + * + * Since: 1.12 + **/ +gboolean +nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other) +{ + _CHECK_WATCHER (watcher, FALSE); + _CHECK_WATCHER (other, FALSE); + + if ( watcher->type != other->type + || !nm_streq0 (watcher->arp_ping.target_host, other->arp_ping.target_host) + || !nm_streq0 (watcher->arp_ping.source_host, other->arp_ping.source_host) + || watcher->arp_ping.init_wait != other->arp_ping.init_wait + || watcher->arp_ping.interval != other->arp_ping.interval + || watcher->arp_ping.missed_max != other->arp_ping.missed_max + || watcher->arp_ping.flags != other->arp_ping.flags) + return FALSE; + + return TRUE; +} + +/** + * nm_team_link_watcher_dup: + * @watcher: the #NMTeamLinkWatcher + * + * Creates a copy of @watcher + * + * Returns: (transfer full): a copy of @watcher + * + * Since: 1.12 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + switch (watcher->type) { + case LINK_WATCHER_ETHTOOL: + return nm_team_link_watcher_new_ethtool (watcher->ethtool.delay_up, + watcher->ethtool.delay_down, + NULL); + break; + case LINK_WATCHER_NSNA_PING: + return nm_team_link_watcher_new_nsna_ping (watcher->nsna_ping.init_wait, + watcher->nsna_ping.interval, + watcher->nsna_ping.missed_max, + watcher->nsna_ping.target_host, + NULL); + break; + case LINK_WATCHER_ARP_PING: + return nm_team_link_watcher_new_arp_ping (watcher->arp_ping.init_wait, + watcher->arp_ping.interval, + watcher->arp_ping.missed_max, + watcher->arp_ping.target_host, + watcher->arp_ping.source_host, + watcher->arp_ping.flags, + NULL); + default: + g_assert_not_reached (); + return NULL; + } +} + +/** + * nm_team_link_watcher_get_name: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the name of the link watcher to be used. + * + * Since: 1.12 + **/ +const char * +nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + return _link_watcher_name[watcher->type]; +} + +/** + * nm_team_link_watcher_get_delay_up: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the delay_up interval (in milliseconds) that elapses between the link + * coming up and the runner beeing notified about it. + * + * Since: 1.12 + **/ +int +nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (watcher->type != LINK_WATCHER_ETHTOOL) + return -1; + return watcher->ethtool.delay_up; +} + +/** + * nm_team_link_watcher_get_delay_down: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the delay_down interval (in milliseconds) that elapses between the link + * going down and the runner beeing notified about it. + * + * Since: 1.12 + **/ +int +nm_team_link_watcher_get_delay_down (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (watcher->type != LINK_WATCHER_ETHTOOL) + return -1; + return watcher->ethtool.delay_down; +} + +/** + * nm_team_link_watcher_get_init_wait: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the init_wait interval (in milliseconds) that the team slave should + * wait before sending the first packet to the target host. + * + * Since: 1.12 + **/ +int +nm_team_link_watcher_get_init_wait (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (!NM_IN_SET (watcher->type, + LINK_WATCHER_NSNA_PING, + LINK_WATCHER_ARP_PING)) + return -1; + return watcher->arp_ping.init_wait; +} + +/** + * nm_team_link_watcher_get_interval: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the interval (in milliseconds) that the team slave should wait between + * sending two check packets to the target host. + * + * Since: 1.12 + **/ +int +nm_team_link_watcher_get_interval (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (!NM_IN_SET (watcher->type, + LINK_WATCHER_NSNA_PING, + LINK_WATCHER_ARP_PING)) + return -1; + return watcher->arp_ping.interval; +} + +/** + * nm_team_link_watcher_get_missed_max: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the number of missed replies after which the link is considered down. + * + * Since: 1.12 + **/ +int +nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (!NM_IN_SET (watcher->type, + LINK_WATCHER_NSNA_PING, + LINK_WATCHER_ARP_PING)) + return -1; + return watcher->arp_ping.missed_max; +} + +/** + * nm_team_link_watcher_get_target_host: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the host name/ip address to be used as destination for the link probing + * packets. + * + * Since: 1.12 + **/ +const char * +nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + return watcher->arp_ping.target_host; +} + +/** + * nm_team_link_watcher_get_source_host: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the ip address to be used as source for the link probing packets. + * + * Since: 1.12 + **/ +const char * +nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + return watcher->arp_ping.source_host; +} + +/** + * nm_team_link_watcher_get_flags: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the arp ping watcher flags. + * + * Since: 1.12 + **/ +NMTeamLinkWatcherArpPingFlags +nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + return watcher->arp_ping.flags; +} + +/*****************************************************************************/ + G_DEFINE_TYPE_WITH_CODE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING, _nm_register_setting (TEAM, NM_SETTING_PRIORITY_HW_BASE)) NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM) @@ -58,6 +585,7 @@ typedef struct { gint runner_sys_prio; gint runner_min_ports; char *runner_agg_select_policy; + GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */ } NMSettingTeamPrivate; /* Keep aligned with _prop_to_keys[] */ @@ -78,6 +606,7 @@ enum { PROP_RUNNER_SYS_PRIO, PROP_RUNNER_MIN_PORTS, PROP_RUNNER_AGG_SELECT_POLICY, + PROP_LINK_WATCHERS, LAST_PROP }; @@ -94,15 +623,13 @@ static const _NMUtilsTeamPropertyKeys _prop_to_keys[LAST_PROP] = { [PROP_RUNNER_HWADDR_POLICY] = { "runner", "hwaddr_policy", NULL, 0 }, [PROP_RUNNER_TX_HASH] = { "runner", "tx_hash", NULL, 0 }, [PROP_RUNNER_TX_BALANCER] = { "runner", "tx_balancer", "name", 0 }, - [PROP_RUNNER_TX_BALANCER_INTERVAL] = { "runner", "tx_balancer", "interval", - NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT }, + [PROP_RUNNER_TX_BALANCER_INTERVAL] = { "runner", "tx_balancer", "balancing_interval", -1 }, [PROP_RUNNER_ACTIVE] = { "runner", "active", NULL, 0 }, [PROP_RUNNER_FAST_RATE] = { "runner", "fast_rate", NULL, 0 }, - [PROP_RUNNER_SYS_PRIO] = { "runner", "sys_prio", NULL, - NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT }, - [PROP_RUNNER_MIN_PORTS] = { "runner", "min_ports", NULL, 0 }, - [PROP_RUNNER_AGG_SELECT_POLICY] = { "runner", "agg_select_policy", NULL, - {.default_str = NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT} }, + [PROP_RUNNER_SYS_PRIO] = { "runner", "sys_prio", NULL, -1 }, + [PROP_RUNNER_MIN_PORTS] = { "runner", "min_ports", NULL, -1 }, + [PROP_RUNNER_AGG_SELECT_POLICY] = { "runner", "agg_select_policy", NULL, 0 }, + [PROP_LINK_WATCHERS] = { "link_watch", NULL, NULL, 0 } }; /** @@ -400,12 +927,12 @@ nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting) * Since: 1.12 **/ const char * -nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, int idx) +nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - g_return_val_if_fail (idx >= 0 && idx < priv->runner_tx_hash->len, NULL); + g_return_val_if_fail (idx < priv->runner_tx_hash->len, NULL); return priv->runner_tx_hash->pdata[idx]; } @@ -420,12 +947,12 @@ nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, int idx) * Since: 1.12 **/ void -nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, int idx) +nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); g_return_if_fail (NM_IS_SETTING_TEAM (setting)); - g_return_if_fail (idx >= 0 && idx < priv->runner_tx_hash->len); + g_return_if_fail (idx < priv->runner_tx_hash->len); g_ptr_array_remove_index (priv->runner_tx_hash, idx); g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH); @@ -465,10 +992,163 @@ nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash) return TRUE; } +/** + * nm_setting_team_get_num_link_watchers: + * @setting: the #NMSettingTeam + * + * Returns: the number of configured link watchers + * + * Since: 1.12 + **/ +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; +} + +/** + * nm_setting_team_get_link_watcher: + * @setting: the #NMSettingTeam + * @idx: index number of the link watcher to return + * + * Returns: (transfer none): the link watcher at index @idx. + * + * Since: 1.12 + **/ +NMTeamLinkWatcher * +nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + 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]; +} + +/** + * nm_setting_team_add_link_watcher: + * @setting: the #NMSettingTeam + * @link_watcher: the link watcher to add + * + * Appends a new link watcher to the setting. + * + * Returns: %TRUE if the link watcher is added; %FALSE if an identical link + * watcher was already there. + * + * Since: 1.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_dup (link_watcher)); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); + return TRUE; +} + +/** + * nm_setting_team_remove_link_watcher: + * @setting: the #NMSettingTeam + * @idx: index number of the link watcher to remove + * + * Removes the link watcher at index #idx. + * + * Since: 1.12 + **/ +void +nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + 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); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); +} + +/** + * nm_setting_team_remove_link_watcher_by_value: + * @setting: the #NMSettingTeam + * @link_watcher: the link watcher to remove + * + * Removes the link watcher entry matching link_watcher. + * + * Returns: %TRUE if the link watcher was found and removed, %FALSE otherwise. + * + * Since: 1.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); + + 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); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_team_clear_link_watchers: + * @setting: the #NMSettingTeam + * + * Removes all configured link watchers. + * + * Since: 1.12 + **/ +void +nm_setting_team_clear_link_watchers (NMSettingTeam *setting) { + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_TEAM (setting)); + + g_ptr_array_set_size (priv->link_watchers, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); +} + +static GVariant * +team_link_watchers_to_dbus (const GValue *prop_value) +{ + return _nm_utils_team_link_watchers_to_variant (g_value_get_boxed (prop_value)); +} + +static void +team_link_watchers_from_dbus (GVariant *dbus_value, + GValue *prop_value) +{ + g_value_take_boxed (prop_value, _nm_utils_team_link_watchers_from_variant (dbus_value)); +} + 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; @@ -507,6 +1187,48 @@ verify (NMSetting *setting, NMConnection *connection, GError **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. */ @@ -519,7 +1241,9 @@ compare_property (NMSetting *setting, const GParamSpec *prop_spec, NMSettingCompareFlags flags) { + NMSettingTeamPrivate *a_priv, *b_priv; NMSettingClass *parent_class; + guint i, j; /* If we are trying to match a connection in order to assume it (and thus * @flags contains INFERRABLE), use the "relaxed" matching for team @@ -532,6 +1256,24 @@ compare_property (NMSetting *setting, NM_SETTING_TEAM_GET_PRIVATE (other)->config, FALSE); } + if (nm_streq0 (prop_spec->name, NM_SETTING_TEAM_LINK_WATCHERS)) { + a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + b_priv = NM_SETTING_TEAM_GET_PRIVATE (other); + + if (a_priv->link_watchers->len != b_priv->link_watchers->len) + return FALSE; + for (i = 0; i < a_priv->link_watchers->len; i++) { + for (j = 0; j < b_priv->link_watchers->len; j++) { + if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i], + b_priv->link_watchers->pdata[j])) { + break; + } + if (j == b_priv->link_watchers->len) + return FALSE; + } + } + return TRUE; + } /* Otherwise chain up to parent to handle generic compare */ parent_class = NM_SETTING_CLASS (nm_setting_team_parent_class); @@ -544,8 +1286,10 @@ 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_sys_prio = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT; - priv->runner_tx_balancer_interval = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT; + 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); } static void @@ -560,6 +1304,7 @@ finalize (GObject *object) 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); G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object); } @@ -604,6 +1349,9 @@ _align_team_properties (NMSettingTeam *setting) nm_setting_team_add_runner_tx_hash (setting, strv[i]); g_strfreev (strv); } + + g_ptr_array_unref (priv->link_watchers); + priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS); } static void @@ -626,51 +1374,45 @@ set_property (GObject *object, guint prop_id, if (priv->notify_peers_count == g_value_get_int (value)) break; priv->notify_peers_count = g_value_get_int (value); - if (priv->notify_peers_count) - align_value = value; + align_value = value; align_config = TRUE; 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); - if (priv->notify_peers_interval) - align_value = value; + align_value = value; align_config = TRUE; 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); - if (priv->mcast_rejoin_count) - align_value = value; + align_value = value; align_config = TRUE; 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); - if (priv->mcast_rejoin_interval) - align_value = value; + align_value = value; align_config = TRUE; 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); - if ( priv->runner - && !nm_streq (priv->runner, - NM_SETTING_TEAM_RUNNER_DEFAULT)) - align_value = value; - align_config = TRUE; + _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); - if ( priv->runner_hwaddr_policy - && !nm_streq (priv->runner_hwaddr_policy, - NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL)) { - align_value = value; - } + align_value = value; align_config = TRUE; break; case PROP_RUNNER_TX_HASH: @@ -685,59 +1427,62 @@ set_property (GObject *object, guint prop_id, 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); - if (priv->runner_tx_balancer) - align_value = 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); - if (priv->runner_tx_balancer_interval != - NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT) - align_value = 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); - if (priv->runner_active) - align_value = 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); - if (priv->runner_fast_rate) - align_value = 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); - if (priv->runner_sys_prio != NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT) - align_value = 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); - if (priv->runner_min_ports) - align_value = 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); - if ( priv->runner_agg_select_policy - && !nm_streq (priv->runner_agg_select_policy, - NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO)) + 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_dup, + (GDestroyNotify) nm_team_link_watcher_unref); + if (priv->link_watchers->len) align_value = value; align_config = TRUE; break; @@ -804,6 +1549,11 @@ get_property (GObject *object, guint prop_id, 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, + (NMUtilsCopyFunc) nm_team_link_watcher_dup, + (GDestroyNotify) nm_team_link_watcher_unref)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1047,6 +1797,33 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NMSettingTeam:link-watchers: + * + * Link watchers configuration for the connection: each link watcher is + * defined by a dictionary, whose keys depend upon the selected link + * watcher. Available link watchers are 'ethtool', 'nsna_ping' and + * 'arp_ping' and it is specified in the dictionary with the key 'name'. + * Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; + * nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; + * arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', + * 'validate-incative', 'send-always'. See teamd.conf man for more details. + * + * Element-Type: NMTeamLinkWatcher + * Since: 1.12 + **/ + g_object_class_install_property + (object_class, PROP_LINK_WATCHERS, + g_param_spec_boxed (NM_SETTING_TEAM_LINK_WATCHERS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + _nm_setting_class_transform_property (parent_class, + NM_SETTING_TEAM_LINK_WATCHERS, + G_VARIANT_TYPE ("aa{sv}"), + team_link_watchers_to_dbus, + team_link_watchers_from_dbus); + /* ---dbus--- * property: interface-name * format: string diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h index 18811374d0..e614faf531 100644 --- a/libnm-core/nm-setting-team.h +++ b/libnm-core/nm-setting-team.h @@ -29,6 +29,80 @@ G_BEGIN_DECLS +/** + * NMTeamLinkWatcherArpPingFlags: + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE: no one among the arp_ping link watcher + * boolean options ('validate_active', 'validate_inactive', 'send_always') is + * enabled (set to true). + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE: the arp_ping link watcher + * option 'validate_active' is enabled (set to true). + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE: the arp_ping link watcher + * option 'validate_inactive' is enabled (set to true). + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS: the arp_ping link watcher option + * 'send_always' is enabled (set to true). + */ +typedef enum { /*< flags >*/ + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE = 0, /*< skip >*/ + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE = (1 << 1), + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE = (1 << 2), + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS = (1 << 3) +} NMTeamLinkWatcherArpPingFlags; + +#define NM_TEAM_LINK_WATCHER_ETHTOOL "ethtool" +#define NM_TEAM_LINK_WATCHER_ARP_PING "arp_ping" +#define NM_TEAM_LINK_WATCHER_NSNA_PING "nsna_ping" + + +typedef struct NMTeamLinkWatcher NMTeamLinkWatcher; + +GType nm_team_link_watcher_get_type (void); + +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcher *nm_team_link_watcher_new_ethtool (gint delay_up, + gint delay_down, + GError **error); +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcher *nm_team_link_watcher_new_nsna_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + GError **error); +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + const char *source_host, + NMTeamLinkWatcherArpPingFlags flags, + GError **error); +NM_AVAILABLE_IN_1_12 +void nm_team_link_watcher_ref (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +void nm_team_link_watcher_unref (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +gboolean nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other); +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcher *nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +const char *nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +int nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +int nm_team_link_watcher_get_delay_down (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +int nm_team_link_watcher_get_init_wait (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +int nm_team_link_watcher_get_interval (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +int nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +const char *nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +const char *nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcherArpPingFlags nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher); + + #define NM_TYPE_SETTING_TEAM (nm_setting_team_get_type ()) #define NM_SETTING_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeam)) #define NM_SETTING_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TEAM, NMSettingTeamClass)) @@ -53,6 +127,7 @@ G_BEGIN_DECLS #define NM_SETTING_TEAM_RUNNER_SYS_PRIO "runner-sys-prio" #define NM_SETTING_TEAM_RUNNER_MIN_PORTS "runner-min-ports" #define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY "runner-agg-select-policy" +#define NM_SETTING_TEAM_LINK_WATCHERS "link-watchers" #define NM_SETTING_TEAM_RUNNER_BROADCAST "broadcast" #define NM_SETTING_TEAM_RUNNER_ROUNDROBIN "roundrobin" @@ -78,6 +153,7 @@ G_BEGIN_DECLS #define NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT 255 #define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO + /** * NMSettingTeam: * @@ -130,11 +206,25 @@ gboolean nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting, NM_AVAILABLE_IN_1_12 guint nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting); NM_AVAILABLE_IN_1_12 -const char *nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, int idx); +const char *nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx); NM_AVAILABLE_IN_1_12 -void nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, int idx); +void nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx); NM_AVAILABLE_IN_1_12 gboolean nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash); +NM_AVAILABLE_IN_1_12 +guint nm_setting_team_get_num_link_watchers (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_12 +NMTeamLinkWatcher * nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx); +NM_AVAILABLE_IN_1_12 +gboolean nm_setting_team_add_link_watcher (NMSettingTeam *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_12 +void nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx); +NM_AVAILABLE_IN_1_12 +gboolean nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_12 +void nm_setting_team_clear_link_watchers (NMSettingTeam *setting); G_END_DECLS #endif /* __NM_SETTING_TEAM_H__ */ diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 6b8d58726a..b7fde275ed 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -78,6 +78,8 @@ void _nm_utils_bytes_from_dbus (GVariant *dbus_value, char * _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize length); +GPtrArray * _nm_utils_team_link_watchers_from_variant (GVariant *value); +GVariant * _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers); /* JSON to GValue conversion macros */ @@ -160,6 +162,30 @@ _nm_utils_json_extract_strv (char *conf, return ret; } +static inline GPtrArray * +_nm_utils_json_extract_ptr_array (char *conf, + _NMUtilsTeamPropertyKeys key, + gboolean is_port) +{ + gs_free 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) + 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_dup (data->pdata[i])); + g_value_unset (t_value); + return ret; +} + static inline void _nm_utils_json_append_gvalue (char **conf, _NMUtilsTeamPropertyKeys key, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index bb91f93733..769f2a7bc1 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -4296,7 +4296,421 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to) /*****************************************************************************/ -#if WITH_JSON_VALIDATION +#if WITH_TEAM + +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 inline 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 inline 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 inline 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; + 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 = json_string_value (json_element); + _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 = 50; + runner_active = TRUE; + runner_sys_prio = 255; + 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; + 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")) + name = strdup (json_string_value (j_val)); + else if (nm_streq (j_key, "target_host")) + target_host = strdup (json_string_value (j_val)); + else if (nm_streq (j_key, "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, "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_ping (val1, val2, val3, 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; + + 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 @@ -4356,9 +4770,8 @@ _nm_utils_team_config_equal (const char *conf1, gboolean port_config) { json_t *json1 = NULL, *json2 = NULL, *json; - json_t *array, *name; gs_free char *dump1 = NULL, *dump2 = NULL; - json_t *value, *property; + json_t *value; json_error_t jerror; const char *key; gboolean ret; @@ -4382,33 +4795,8 @@ _nm_utils_team_config_equal (const char *conf1, * configuration. Add them with the default value if necessary, depending * on the configuration type. */ - for (i = 0, json = json1; i < 2; i++, json = json2) { - if (port_config) { - property = json_object_get (json, "link_watch"); - if (!property) { - property = json_object (); - json_object_set_new (property, "name", json_string ("ethtool")); - json_object_set_new (json, "link_watch", property); - } - } else { - property = json_object_get (json, "runner"); - if (!property) { - property = json_object (); - json_object_set_new (property, "name", json_string ("roundrobin")); - json_object_set_new (json, "runner", property); - } else if ( (name = json_object_get (property, "name")) - && NM_IN_STRSET (json_string_value (name), "lacp", "loadbalance")) { - /* Add default tx_hash when missing */ - if (!json_object_get (property, "tx_hash")) { - array = json_array (); - json_array_append_new (array, json_string ("eth")); - json_array_append_new (array, json_string ("ipv4")); - json_array_append_new (array, json_string ("ipv6")); - json_object_set_new (property, "tx_hash", array); - } - } - } - } + 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 */ @@ -4434,61 +4822,6 @@ out: } - -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); -} - GValue * _nm_utils_team_config_get (const char *conf, const char *key, @@ -4500,7 +4833,6 @@ _nm_utils_team_config_get (const char *conf, json_t *json_element; GValue *value = NULL; json_error_t jerror; - const char *runner = NULL; if (!key) return NULL; @@ -4514,38 +4846,16 @@ _nm_utils_team_config_get (const char *conf, /* 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_add_object (json, "link_watch", "name", NULL, json_string ("ethtool")); - } else { - /* 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 (!port_config) + _json_team_add_defaults (json, port_config, TRUE); - 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); - } - } + /* 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); @@ -4563,6 +4873,31 @@ _nm_utils_team_config_get (const char *conf, } 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 ((GDestroyNotify) g_free); json_t *str_element; @@ -4602,8 +4937,12 @@ _nm_utils_team_config_set (char **conf, json_error_t jerror; gboolean updated = FALSE; char **strv; + GPtrArray *array; const char *iter_key = key; int i; + NMTeamLinkWatcher *watcher; + + g_return_val_if_fail (key, FALSE); json = json_loads (*conf?: "{}", JSON_REJECT_DUPLICATES, &jerror); if (!json) @@ -4611,38 +4950,7 @@ _nm_utils_team_config_set (char **conf, /* no new value? delete element */ if (!value) { - json_element = json; - json_link = NULL; - - if (key2) { - json_link = json; - json_element = json_object_get (json, key); - if (!json_element) - goto done; - iter_key = key2; - } - if (key3) { - json_link = json_element; - json_element = json_object_get (json_element, key2); - if (!json_element) - goto done; - iter_key = key3; - } - if (json_object_del (json_element, iter_key) != 0) - goto done; - - updated = TRUE; - - /* 1st level key only */ - if (!json_link) - goto done; - - if (json_object_size (json_element) == 0) - json_object_del (json_link, (key3 ? key2 : key)); - - if (key3 && json_object_size (json_link) == 0) - json_object_del (json, key); - + updated = _json_del_object (json, key, key2, key3); goto done; } @@ -4655,14 +4963,51 @@ _nm_utils_team_config_set (char **conf, else if (G_VALUE_HOLDS_BOOLEAN (value)) json_value = json_boolean (g_value_get_boolean (value)); else if (G_VALUE_HOLDS_BOXED (value)) { - strv = g_value_get_boxed (value); - if (strv) { + if (nm_streq (key, "link_watch")) { + array = g_value_get_boxed (value); + if (!array || !array->len) { + updated = FALSE; + goto done; + } + + /* + * json_value: will hold the final link_watcher json (array) object + * json_element: is the next link_watcher to append to json_value + * json_link: used to transit the json_value from a single link_watcher + * object to an array of link watcher objects + */ + json_value = NULL; + for (i = 0; i < array->len; i++) { + watcher = array->pdata[i]; + json_element = _nm_utils_team_link_watcher_to_json (watcher); + if (!json_element) + continue; + if (!json_value) { + json_value = json_element; + continue; + } + if (!json_is_array (json_value)) { + json_link = json_value; + json_value = json_array (); + json_array_append_new (json_value, json_link); + } + json_array_append_new (json_value, json_element); + } + } else if ( nm_streq (key, "runner") + && nm_streq0 (key2, "tx_hash")) { + strv = g_value_get_boxed (value); + if (!strv) { + updated = FALSE; + goto done; + } json_value = json_array (); for (i = 0; strv[i]; i++) json_array_append_new (json_value, json_string (strv[i])); - } else - return FALSE; - } else { + } else { + updated = FALSE; + goto done; + } + } else { /* G_VALUE_HOLDS_? */ g_assert_not_reached (); updated = FALSE; goto done; @@ -4695,8 +5040,10 @@ _nm_utils_team_config_set (char **conf, done: if (updated) { + _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") + && nm_streq0 (key2, "name"))); g_free (*conf); - *conf = json_dumps (json, 0); + *conf = json_dumps (json, JSON_PRESERVE_ORDER); /* Don't save an empty config */ if (nm_streq0 (*conf, "{}")) { g_free (*conf); @@ -4707,7 +5054,7 @@ done: return updated; } -#else /* !WITH_JSON_VALIDATION */ +#else /* !WITH_TEAM */ gboolean nm_utils_is_json_object (const char *str, GError **error) @@ -4783,6 +5130,202 @@ _nm_utils_team_config_set (char **conf, } #endif +/** + * _nm_utils_team_link_watchers_to_variant: + * @link_watchers: (element-type NMTeamLinkWatcher): array of #NMTeamLinkWatcher + * + * Utility function to convert a #GPtrArray of #NMTeamLinkWatcher objects + * representing link watcher configuration for team devices into a #GVariant + * of type 'aa{sv}' representing an array of link watchers. + * + * Returns: (transfer none): a new floating #GVariant representing link watchers. + **/ +GVariant * +_nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers) +{ + GVariantBuilder builder; + int i; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (!link_watchers) + goto end; + + for (i = 0; i < link_watchers->len; i++) { + NMTeamLinkWatcher *watcher = link_watchers->pdata[i]; + GVariantBuilder watcher_builder; + const char *name; + int int_val; + NMTeamLinkWatcherArpPingFlags flags; + + g_variant_builder_init (&watcher_builder, G_VARIANT_TYPE ("a{sv}")); + + name = nm_team_link_watcher_get_name (watcher); + g_variant_builder_add (&watcher_builder, "{sv}", + "name", + g_variant_new_string (name)); + + if nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL) { + int_val = nm_team_link_watcher_get_delay_up (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "delay-up", + g_variant_new_int32 (int_val)); + } + int_val = nm_team_link_watcher_get_delay_down (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "delay-down", + g_variant_new_int32 (int_val)); + } + g_variant_builder_add (&builder, "a{sv}", &watcher_builder); + continue; + } + + /* Common properties for arp_ping and nsna_ping link watchers */ + int_val = nm_team_link_watcher_get_init_wait (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "init-wait", + g_variant_new_int32 (int_val)); + } + int_val = nm_team_link_watcher_get_interval (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "interval", + g_variant_new_int32 (int_val)); + } + int_val = nm_team_link_watcher_get_missed_max (watcher); + if (int_val != 3) { + g_variant_builder_add (&watcher_builder, "{sv}", + "missed-max", + g_variant_new_int32 (int_val)); + } + g_variant_builder_add (&watcher_builder, "{sv}", + "target-host", + g_variant_new_string (nm_team_link_watcher_get_target_host (watcher))); + + if nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING) { + g_variant_builder_add (&builder, "a{sv}", &watcher_builder); + continue; + } + + /* arp_ping watcher only */ + g_variant_builder_add (&watcher_builder, "{sv}", + "source-host", + g_variant_new_string (nm_team_link_watcher_get_source_host (watcher))); + flags = nm_team_link_watcher_get_flags (watcher); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE) { + g_variant_builder_add (&watcher_builder, "{sv}", + "validate-active", + g_variant_new_boolean (TRUE)); + } + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE) { + g_variant_builder_add (&watcher_builder, "{sv}", + "validate-inactive", + g_variant_new_boolean (TRUE)); + } + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS) { + g_variant_builder_add (&watcher_builder, "{sv}", + "send-always", + g_variant_new_boolean (TRUE)); + } + g_variant_builder_add (&builder, "a{sv}", &watcher_builder); + } +end: + return g_variant_builder_end (&builder); +} + +/** + * _nm_utils_team_link_watchers_from_variant: + * @value: a #GVariant of type 'aa{sv}' + * + * Utility function to convert a #GVariant representing a list of team link + * watchers int a #GPtrArray of #NMTeamLinkWatcher objects. + * + * Returns: (transfer full) (element-type NMTeamLinkWatcher): a newly allocated + * #GPtrArray of #NMTeamLinkWatcher objects. + **/ +GPtrArray * +_nm_utils_team_link_watchers_from_variant (GVariant *value) +{ + GPtrArray *link_watchers; + GVariantIter iter; + GVariant *watcher_var; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), NULL); + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_variant_iter_init (&iter, value); + + while (g_variant_iter_next (&iter, "@a{sv}", &watcher_var)) { + NMTeamLinkWatcher *watcher; + const char *name; + int val1, val2, val3 = 0; + const char *target_host = NULL, *source_host = NULL; + gboolean bval; + NMTeamLinkWatcherArpPingFlags flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE; + GError *error = NULL; + + if (!g_variant_lookup (watcher_var, "name", "&s", &name)) + goto next; + if (!NM_IN_STRSET (name, + NM_TEAM_LINK_WATCHER_ETHTOOL, + NM_TEAM_LINK_WATCHER_ARP_PING, + NM_TEAM_LINK_WATCHER_NSNA_PING)) { + goto next; + } + + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { + if (!g_variant_lookup (watcher_var, "delay-up", "i", &val1)) + val1 = 0; + if (!g_variant_lookup (watcher_var, "delay-down", "i", &val2)) + val2 = 0; + watcher = nm_team_link_watcher_new_ethtool (val1, val2, &error); + } else { + if (!g_variant_lookup (watcher_var, "target-host", "&s", &target_host)) + goto next; + if (!g_variant_lookup (watcher_var, "init_wait", "i", &val1)) + val1 = 0; + if (!g_variant_lookup (watcher_var, "interval", "i", &val2)) + val2 = 0; + if (!g_variant_lookup (watcher_var, "missed-max", "i", &val3)) + val3 = 3; + if nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) { + if (!g_variant_lookup (watcher_var, "source-host", "&s", &source_host)) + goto next; + if (!g_variant_lookup (watcher_var, "validate-active", "b", &bval)) + bval = FALSE; + if (bval) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; + if (!g_variant_lookup (watcher_var, "validate-inactive", "b", &bval)) + bval = FALSE; + if (bval) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE; + if (!g_variant_lookup (watcher_var, "send-always", "b", &bval)) + bval = FALSE; + if (bval) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; + watcher = nm_team_link_watcher_new_arp_ping (val1, val2, val3, + target_host, source_host, + flags, &error); + } else + watcher = nm_team_link_watcher_new_nsna_ping (val1, val2, val3, + target_host, &error); + } + if (!watcher) { + g_clear_error (&error); + goto next; + } + + g_ptr_array_add (link_watchers, watcher); +next: + g_variant_unref (watcher_var); + } + + return link_watchers; +} + static char * attribute_escape (const char *src, char c1, char c2) { diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 21337a9d40..8a30aa5d92 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -5858,7 +5858,7 @@ test_nm_utils_check_valid_json (void) { _json_config_check_valid (NULL, FALSE); _json_config_check_valid ("", FALSE); -#if WITH_JSON_VALIDATION +#if WITH_TEAM _json_config_check_valid ("{ }", TRUE); _json_config_check_valid ("{ \"a\" : 1 }", TRUE); _json_config_check_valid ("{ \"a\" : }", FALSE); @@ -5883,7 +5883,7 @@ _team_config_equal_check (const char *conf1, static void test_nm_utils_team_config_equal (void) { -#if WITH_JSON_VALIDATION +#if WITH_TEAM _team_config_equal_check ("", "", TRUE, TRUE); _team_config_equal_check ("{}", "{ }", diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index c7d638244d..a5b2ecc255 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -628,7 +628,7 @@ test_team_conf_read_valid (void) static void test_team_conf_read_invalid (void) { -#if WITH_JSON_VALIDATION +#if WITH_TEAM GKeyFile *keyfile = NULL; gs_unref_object NMConnection *con = NULL; NMSettingTeam *s_team; diff --git a/libnm-core/tests/test-setting-8021x.c b/libnm-core/tests/test-setting-8021x.c deleted file mode 100644 index 268a9f52d9..0000000000 --- a/libnm-core/tests/test-setting-8021x.c +++ /dev/null @@ -1,404 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2008 - 2011 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include <string.h> - -#include "nm-utils.h" -#include "nm-setting-connection.h" -#include "nm-setting-8021x.h" - -#include "nm-utils/nm-test-utils.h" - -static void -compare_blob_data (const char *test, - const char *key_path, - GBytes *key) -{ - char *contents = NULL; - gsize len = 0; - GError *error = NULL; - gboolean success; - - g_assert (key && g_bytes_get_size (key) > 0); - - success = g_file_get_contents (key_path, &contents, &len, &error); - nmtst_assert_success (success, error); - - g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key)); - - g_free (contents); -} - -static void -check_scheme_path (GBytes *value, const char *path) -{ - const guint8 *p; - - g_assert (value); - - p = g_bytes_get_data (value, NULL); - g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0); - p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); - g_assert (memcmp (p, path, strlen (path)) == 0); - p += strlen (path); - g_assert (*p == '\0'); -} - -static void -test_private_key_import (const char *path, - const char *password, - NMSetting8021xCKScheme scheme) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - NMSetting8021xCKFormat tmp_fmt; - GError *error = NULL; - GBytes *tmp_key = NULL, *client_cert = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_private_key (s_8021x, - path, - password, - scheme, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x); - g_assert (tmp_fmt == format); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (pw != NULL); - g_assert_cmpstr (pw, ==, password); - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - tmp_key = nm_setting_802_1x_get_private_key_blob (s_8021x); - compare_blob_data ("private-key-import", path, tmp_key); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); - check_scheme_path (tmp_key, path); - g_bytes_unref (tmp_key); - } else - g_assert_not_reached (); - - /* If it's PKCS#12 ensure the client cert is the same value */ - if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); - g_assert (tmp_key); - - g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL); - g_assert (client_cert); - - /* make sure they are the same */ - g_assert (g_bytes_equal (tmp_key, client_cert)); - - g_bytes_unref (tmp_key); - g_bytes_unref (client_cert); - } - - g_object_unref (s_8021x); -} - -static void -test_phase2_private_key_import (const char *path, - const char *password, - NMSetting8021xCKScheme scheme) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - NMSetting8021xCKFormat tmp_fmt; - GError *error = NULL; - GBytes *tmp_key = NULL, *client_cert = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - path, - password, - scheme, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); - g_assert (tmp_fmt == format); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - tmp_key = nm_setting_802_1x_get_phase2_private_key_blob (s_8021x); - compare_blob_data ("phase2-private-key-import", path, tmp_key); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); - check_scheme_path (tmp_key, path); - g_bytes_unref (tmp_key); - } else - g_assert_not_reached (); - - /* If it's PKCS#12 ensure the client cert is the same value */ - if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); - g_assert (tmp_key); - - g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL); - g_assert (client_cert); - - /* make sure they are the same */ - g_assert (g_bytes_equal (tmp_key, client_cert)); - - g_bytes_unref (tmp_key); - g_bytes_unref (client_cert); - } - - g_object_unref (s_8021x); -} - -static void -test_wrong_password_keeps_data (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Now try to set it to something that's not a certificate */ - format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - success = nm_setting_802_1x_set_private_key (s_8021x, - "Makefile.am", - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_no_success (success, error); - g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - g_clear_error (&error); - - /* Make sure the password hasn't changed */ - pw = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - g_object_unref (s_8021x); -} - -static void -test_clear_private_key (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - /* Now clear it */ - success = nm_setting_802_1x_set_private_key (s_8021x, - NULL, - NULL, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - NULL, - &error); - nmtst_assert_success (success, error); - - /* Ensure the password is also now clear */ - g_assert (!nm_setting_802_1x_get_private_key_password (s_8021x)); - - g_object_unref (s_8021x); -} - -static void -test_wrong_phase2_password_keeps_data (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Now try to set it to something that's not a certificate */ - format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - "Makefile.am", - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_no_success (success, error); - g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - g_clear_error (&error); - - /* Make sure the password hasn't changed */ - pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - g_object_unref (s_8021x); -} - -static void -test_clear_phase2_private_key (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - /* Now clear it */ - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - NULL, - NULL, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - NULL, - &error); - nmtst_assert_success (success, error); - - /* Ensure the password is also now clear */ - g_assert (!nm_setting_802_1x_get_phase2_private_key_password (s_8021x)); - - g_object_unref (s_8021x); -} - -static void -do_8021x_test (gconstpointer test_data) -{ - char **parts, *path, *password; - - parts = g_strsplit ((const char *) test_data, ", ", -1); - g_assert_cmpint (g_strv_length (parts), ==, 2); - - path = g_build_filename (TEST_CERT_DIR, parts[0], NULL); - password = parts[1]; - - /* Test phase1 and phase2 path scheme */ - test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); - test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); - - /* Test phase1 and phase2 blob scheme */ - test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); - test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); - - /* Test that using a wrong password does not change existing data */ - test_wrong_password_keeps_data (path, password); - test_wrong_phase2_password_keeps_data (path, password); - - /* Test clearing the private key */ - test_clear_private_key (path, password); - test_clear_phase2_private_key (path, password); - - g_free (path); - g_strfreev (parts); -} - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init (&argc, &argv, TRUE); - - g_test_add_data_func ("/libnm/setting-8021x/key-and-cert", - "test_key_and_cert.pem, test", - do_8021x_test); - g_test_add_data_func ("/libnm/setting-8021x/key-only", - "test-key-only.pem, test", - do_8021x_test); - g_test_add_data_func ("/libnm/setting-8021x/pkcs8-enc-key", - "pkcs8-enc-key.pem, 1234567890", - do_8021x_test); - g_test_add_data_func ("/libnm/setting-8021x/pkcs12", - "test-cert.p12, test", - do_8021x_test); - - return g_test_run (); -} - diff --git a/libnm-core/tests/test-setting-bond.c b/libnm-core/tests/test-setting-bond.c deleted file mode 100644 index e6a65bba64..0000000000 --- a/libnm-core/tests/test-setting-bond.c +++ /dev/null @@ -1,247 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-utils.h" -#include "nm-setting-bond.h" -#include "nm-connection.h" -#include "nm-simple-connection.h" -#include "nm-setting-connection.h" -#include "nm-errors.h" - -#include "nm-utils/nm-test-utils.h" - -static void -create_bond_connection (NMConnection **con, NMSettingBond **s_bond) -{ - NMSettingConnection *s_con; - - g_assert (con); - g_assert (s_bond); - - *con = nmtst_create_minimal_connection ("bond", - NULL, - NM_SETTING_BOND_SETTING_NAME, - &s_con); - g_assert (*con); - g_assert (s_con); - - g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "bond0", NULL); - - *s_bond = (NMSettingBond *) nm_setting_bond_new (); - g_assert (*s_bond); - - nm_connection_add_setting (*con, NM_SETTING (*s_bond)); -} - -#define test_verify_options(exp, ...) \ - G_STMT_START { \ - const char *__opts[] = { __VA_ARGS__ , NULL }; \ - \ - _test_verify_options (__opts, exp); \ - } G_STMT_END - -static void -_test_verify_options (const char **options, gboolean expected_result) -{ - gs_unref_object NMConnection *con = NULL; - NMSettingBond *s_bond; - GError *error = NULL; - gboolean success; - const char **option; - - create_bond_connection (&con, &s_bond); - - for (option = options; option[0] && option[1]; option += 2) - g_assert (nm_setting_bond_add_option (s_bond, option[0], option[1])); - - if (expected_result) { - nmtst_assert_connection_verifies_and_normalizable (con); - nmtst_connection_normalize (con); - success = nm_setting_verify ((NMSetting *) s_bond, con, &error); - nmtst_assert_success (success, error); - } else { - nmtst_assert_connection_unnormalizable (con, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY); - } -} - -static void -test_verify (void) -{ - test_verify_options (TRUE, - "mode", "3", - "arp_interval", "0"); - test_verify_options (FALSE, - /* arp_interval not supported in balance-alb mode */ - "mode", "balance-alb", - "arp_interval", "1", - "arp_ip_target", "1.2.3.4"); - test_verify_options (FALSE, - /* arp_ip_target requires arp_interval */ - "mode", "balance-rr", - "arp_ip_target", "1.2.3.4"); - test_verify_options (TRUE, - "mode", "balance-rr", - "arp_interval", "1", - "arp_ip_target", "1.2.3.4"); - test_verify_options (FALSE, - /* num_grat_arp, num_unsol_na cannot be different */ - "mode", "balance-rr", - "num_grat_arp", "3", - "num_unsol_na", "4"); - test_verify_options (TRUE, - "mode", "balance-rr", - "num_grat_arp", "5", - "num_unsol_na", "5"); - test_verify_options (TRUE, - "mode", "active-backup", - "primary", "eth0"); - test_verify_options (FALSE, - /* primary requires mode=active-backup */ - "mode", "802.3ad", - "primary", "eth0"); - test_verify_options (TRUE, - "mode", "802.3ad", - "lacp_rate", "fast"); - test_verify_options (FALSE, - /* lacp_rate=fast requires mode=802.3ad */ - "mode", "balance-rr", - "lacp_rate", "fast"); - test_verify_options (TRUE, - "mode", "802.3ad", - "ad_actor_system", "ae:00:11:33:44:55"); -} - -static void -test_compare_options (gboolean exp_res, const char **opts1, const char **opts2) -{ - gs_unref_object NMSettingBond *s_bond1 = NULL, *s_bond2 = NULL; - const char **p; - - s_bond1 = (NMSettingBond *) nm_setting_bond_new (); - g_assert (s_bond1); - s_bond2 = (NMSettingBond *) nm_setting_bond_new (); - g_assert (s_bond2); - - for (p = opts1; p[0] && p[1]; p += 2) - g_assert (nm_setting_bond_add_option (s_bond1, p[0], p[1])); - - for (p = opts2; p[0] && p[1]; p += 2) - g_assert (nm_setting_bond_add_option (s_bond2, p[0], p[1])); - - g_assert_cmpint (nm_setting_compare ((NMSetting *) s_bond1, - (NMSetting *) s_bond2, - NM_SETTING_COMPARE_FLAG_EXACT), - ==, - exp_res); -} - -static void -test_compare (void) -{ - test_compare_options (TRUE, - ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), - ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL })); - test_compare_options (FALSE, - ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), - ((const char *[]){ "mode", "balance-rr", "miimon", "2", NULL })); - - /* ignore default values */ - test_compare_options (TRUE, - ((const char *[]){ "miimon", "1", NULL }), - ((const char *[]){ "miimon", "1", "updelay", "0", NULL })); - - /* special handling of num_grat_arp, num_unsol_na */ - test_compare_options (FALSE, - ((const char *[]){ "num_grat_arp", "2", NULL }), - ((const char *[]){ "num_grat_arp", "1", NULL })); - test_compare_options (TRUE, - ((const char *[]){ "num_grat_arp", "3", NULL }), - ((const char *[]){ "num_unsol_na", "3", NULL })); - test_compare_options (TRUE, - ((const char *[]){ "num_grat_arp", "4", NULL }), - ((const char *[]){ "num_unsol_na", "4", "num_grat_arp", "4", NULL })); -} - -static void -test_normalize_options (const char **opts1, const char **opts2) -{ - gs_unref_object NMConnection *con = NULL; - NMSettingBond *s_bond; - GError *error = NULL; - gboolean success; - const char **p; - int num = 0; - - create_bond_connection (&con, &s_bond); - - for (p = opts1; p[0] && p[1]; p += 2) - g_assert (nm_setting_bond_add_option (s_bond, p[0], p[1])); - - nmtst_assert_connection_verifies_and_normalizable (con); - nmtst_connection_normalize (con); - success = nm_setting_verify ((NMSetting *) s_bond, con, &error); - nmtst_assert_success (success, error); - - for (p = opts2; p[0] && p[1]; p += 2) { - g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, p[0]), ==, p[1]); - num++; - } - - g_assert_cmpint (num, ==, nm_setting_bond_get_num_options (s_bond)); -} - -static void -test_normalize (void) -{ - test_normalize_options ( - ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL }), - ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "1", "miimon", "1", NULL }), - ((const char *[]){ "mode", "active-backup", "miimon", "1", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "balance-alb", "tlb_dynamic_lb", "1", NULL }), - ((const char *[]){ "mode", "balance-alb", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL }), - ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "balance-rr", "ad_actor_sys_prio", "4", "packets_per_slave", "3", NULL }), - ((const char *[]){ "mode", "balance-rr", "packets_per_slave", "3", NULL })); -} - -#define TPATH "/libnm/settings/bond/" - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init (&argc, &argv, TRUE); - - g_test_add_func (TPATH "verify", test_verify); - g_test_add_func (TPATH "compare", test_compare); - g_test_add_func (TPATH "normalize", test_normalize); - - return g_test_run (); -} diff --git a/libnm-core/tests/test-setting-dcb.c b/libnm-core/tests/test-setting-dcb.c deleted file mode 100644 index 55c1c5162c..0000000000 --- a/libnm-core/tests/test-setting-dcb.c +++ /dev/null @@ -1,321 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2013 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include <string.h> - -#include "nm-utils.h" -#include "nm-setting-dcb.h" -#include "nm-connection.h" -#include "nm-errors.h" - -#include "nm-utils/nm-test-utils.h" - -#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ - NM_SETTING_DCB_FLAG_ADVERTISE | \ - NM_SETTING_DCB_FLAG_WILLING) - -static void -test_dcb_flags_valid (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - guint i; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0); - - g_object_set (G_OBJECT (s_dcb), - NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL, - NULL); - /* Priority Group Bandwidth must total 100% */ - for (i = 0; i < 7; i++) - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); - - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); - g_assert_no_error (error); - g_assert (success); - - g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL); -} - -#define TEST_FLAG(p, f, v) \ -{ \ - /* GObject property min/max should ensure the property does not get set to \ - * the invalid value, so we ensure the value we just tried to set is 0 and \ - * that verify is successful since the property never got set. \ - */ \ - g_object_set (G_OBJECT (s_dcb), p, v, NULL); \ - g_assert_cmpint (f (s_dcb), ==, 0); \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_no_error (error); \ - g_assert (success); \ -} - -static void -test_dcb_flags_invalid (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags, - (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1); - g_test_assert_expected_messages (); -} - -#define TEST_APP_PRIORITY(lcprop, ucprop, v) \ -{ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ - \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \ - g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \ - \ - /* Assert that the setting is invalid while the app is disabled unless v is default */ \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - if (v >= 0) { \ - g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ - g_assert (success == FALSE); \ - } else { \ - g_assert_no_error (error); \ - g_assert (success); \ - } \ - g_clear_error (&error); \ - \ - /* Set the enable flag and re-verify, this time it should be valid */ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_no_error (error); \ - g_assert (success); \ - \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \ -} - -static void -test_dcb_app_priorities (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - /* Defaults */ - g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1); - g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1); - g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1); - - TEST_APP_PRIORITY (fcoe, FCOE, 6); - TEST_APP_PRIORITY (iscsi, ISCSI, 5); - TEST_APP_PRIORITY (fip, FIP, 4); - - TEST_APP_PRIORITY (fcoe, FCOE, -1); - TEST_APP_PRIORITY (iscsi, ISCSI, -1); - TEST_APP_PRIORITY (fip, FIP, -1); -} - -#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \ -{ \ - /* Assert that setting the value gets the same value back out */ \ - nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \ - g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \ - \ - if (verify) { \ - if (val != 0) { \ - /* Assert that verify fails because the flags do not include 'enabled' \ - * and a value has been set. \ - */ \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ - g_assert (success == FALSE); \ - g_clear_error (&error); \ - } \ - \ - /* Assert that adding the 'enabled' flag verifies the setting */ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_no_error (error); \ - g_assert (success); \ - } \ - \ - /* Reset everything */ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ - nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \ -} - -/* If Priority Groups are enabled, PG bandwidth must equal 100% */ -#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \ -{ \ - guint x; \ - for (x = 0; x < 7; x++) \ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \ -} - -static void -test_dcb_priorities_valid (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - guint i; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - for (i = 0; i < 8; i++) - TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE); - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) { - TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE); - TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE); - } - - /* Clear PG bandwidth from earlier tests */ - for (i = 0; i < 8; i++) - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0); - - /* Priority Group Bandwidth must add up to 100% if enabled, which requires - * some dancing for verifying individual values here. - */ - for (i = 0; i < 8; i++) { - guint other = 7 - (i % 8); - - /* Set another priority group to the remaining bandwidth */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i); - TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE); - - /* Set another priority group to the remaining bandwidth */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i)); - TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE); - - /* Clear remaining bandwidth */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0); - } - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) { - TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE); - TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE); - } - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) - TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE); - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) { - TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE); - TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE); - } -} - -static void -test_dcb_bandwidth_sums (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - /* Assert that setting the value gets the same value back out */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); - - /* Assert verify success when sums total 100% */ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); - g_assert_no_error (error); - g_assert (success); - - /* Assert verify fails when sums do not total 100% */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20); - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); - g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); - g_assert (success == FALSE); - g_clear_error (&error); -} - -#define TPATH "/libnm/settings/dcb/" - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init (&argc, &argv, TRUE); - - g_test_add_func (TPATH "flags-valid", test_dcb_flags_valid); - g_test_add_func (TPATH "flags-invalid", test_dcb_flags_invalid); - g_test_add_func (TPATH "app-priorities", test_dcb_app_priorities); - g_test_add_func (TPATH "priorities", test_dcb_priorities_valid); - g_test_add_func (TPATH "bandwidth-sums", test_dcb_bandwidth_sums); - - return g_test_run (); -} - diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c new file mode 100644 index 0000000000..7710cffe38 --- /dev/null +++ b/libnm-core/tests/test-setting.c @@ -0,0 +1,1311 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2017 Red Hat, Inc. + * + */ + +#include "nm-default.h" + +#include <string.h> + +#include "nm-utils.h" +#include "nm-setting-8021x.h" +#include "nm-setting-bond.h" +#include "nm-setting-dcb.h" +#include "nm-setting-team.h" +#include "nm-setting-team-port.h" +#include "nm-connection.h" +#include "nm-simple-connection.h" +#include "nm-setting-connection.h" +#include "nm-errors.h" + +#include "nm-utils/nm-test-utils.h" + +/*****************************************************************************/ + +static void +compare_blob_data (const char *test, + const char *key_path, + GBytes *key) +{ + char *contents = NULL; + gsize len = 0; + GError *error = NULL; + gboolean success; + + g_assert (key && g_bytes_get_size (key) > 0); + + success = g_file_get_contents (key_path, &contents, &len, &error); + nmtst_assert_success (success, error); + + g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key)); + + g_free (contents); +} + +static void +check_scheme_path (GBytes *value, const char *path) +{ + const guint8 *p; + + g_assert (value); + + p = g_bytes_get_data (value, NULL); + g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0); + p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + g_assert (memcmp (p, path, strlen (path)) == 0); + p += strlen (path); + g_assert (*p == '\0'); +} + +static void +test_private_key_import (const char *path, + const char *password, + NMSetting8021xCKScheme scheme) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + NMSetting8021xCKFormat tmp_fmt; + GError *error = NULL; + GBytes *tmp_key = NULL, *client_cert = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + scheme, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x); + g_assert (tmp_fmt == format); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (pw != NULL); + g_assert_cmpstr (pw, ==, password); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + tmp_key = nm_setting_802_1x_get_private_key_blob (s_8021x); + compare_blob_data ("private-key-import", path, tmp_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); + check_scheme_path (tmp_key, path); + g_bytes_unref (tmp_key); + } else + g_assert_not_reached (); + + /* If it's PKCS#12 ensure the client cert is the same value */ + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); + g_assert (tmp_key); + + g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL); + g_assert (client_cert); + + /* make sure they are the same */ + g_assert (g_bytes_equal (tmp_key, client_cert)); + + g_bytes_unref (tmp_key); + g_bytes_unref (client_cert); + } + + g_object_unref (s_8021x); +} + +static void +test_phase2_private_key_import (const char *path, + const char *password, + NMSetting8021xCKScheme scheme) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + NMSetting8021xCKFormat tmp_fmt; + GError *error = NULL; + GBytes *tmp_key = NULL, *client_cert = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + scheme, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); + g_assert (tmp_fmt == format); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + tmp_key = nm_setting_802_1x_get_phase2_private_key_blob (s_8021x); + compare_blob_data ("phase2-private-key-import", path, tmp_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); + check_scheme_path (tmp_key, path); + g_bytes_unref (tmp_key); + } else + g_assert_not_reached (); + + /* If it's PKCS#12 ensure the client cert is the same value */ + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); + g_assert (tmp_key); + + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL); + g_assert (client_cert); + + /* make sure they are the same */ + g_assert (g_bytes_equal (tmp_key, client_cert)); + + g_bytes_unref (tmp_key); + g_bytes_unref (client_cert); + } + + g_object_unref (s_8021x); +} + +static void +test_wrong_password_keeps_data (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Now try to set it to something that's not a certificate */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_private_key (s_8021x, + "Makefile.am", + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_no_success (success, error); + g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + g_clear_error (&error); + + /* Make sure the password hasn't changed */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + g_object_unref (s_8021x); +} + +static void +test_clear_private_key (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + /* Now clear it */ + success = nm_setting_802_1x_set_private_key (s_8021x, + NULL, + NULL, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NULL, + &error); + nmtst_assert_success (success, error); + + /* Ensure the password is also now clear */ + g_assert (!nm_setting_802_1x_get_private_key_password (s_8021x)); + + g_object_unref (s_8021x); +} + +static void +test_wrong_phase2_password_keeps_data (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Now try to set it to something that's not a certificate */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + "Makefile.am", + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_no_success (success, error); + g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + g_clear_error (&error); + + /* Make sure the password hasn't changed */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + g_object_unref (s_8021x); +} + +static void +test_clear_phase2_private_key (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + /* Now clear it */ + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + NULL, + NULL, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NULL, + &error); + nmtst_assert_success (success, error); + + /* Ensure the password is also now clear */ + g_assert (!nm_setting_802_1x_get_phase2_private_key_password (s_8021x)); + + g_object_unref (s_8021x); +} + +static void +test_8021x (gconstpointer test_data) +{ + char **parts, *path, *password; + + parts = g_strsplit ((const char *) test_data, ", ", -1); + g_assert_cmpint (g_strv_length (parts), ==, 2); + + path = g_build_filename (TEST_CERT_DIR, parts[0], NULL); + password = parts[1]; + + /* Test phase1 and phase2 path scheme */ + test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); + test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); + + /* Test phase1 and phase2 blob scheme */ + test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); + test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); + + /* Test that using a wrong password does not change existing data */ + test_wrong_password_keeps_data (path, password); + test_wrong_phase2_password_keeps_data (path, password); + + /* Test clearing the private key */ + test_clear_private_key (path, password); + test_clear_phase2_private_key (path, password); + + g_free (path); + g_strfreev (parts); +} + +/*****************************************************************************/ + +static void +create_bond_connection (NMConnection **con, NMSettingBond **s_bond) +{ + NMSettingConnection *s_con; + + g_assert (con); + g_assert (s_bond); + + *con = nmtst_create_minimal_connection ("bond", + NULL, + NM_SETTING_BOND_SETTING_NAME, + &s_con); + g_assert (*con); + g_assert (s_con); + + g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "bond0", NULL); + + *s_bond = (NMSettingBond *) nm_setting_bond_new (); + g_assert (*s_bond); + + nm_connection_add_setting (*con, NM_SETTING (*s_bond)); +} + +#define test_verify_options(exp, ...) \ + G_STMT_START { \ + const char *__opts[] = { __VA_ARGS__ , NULL }; \ + \ + _test_verify_options (__opts, exp); \ + } G_STMT_END + +static void +_test_verify_options (const char **options, gboolean expected_result) +{ + gs_unref_object NMConnection *con = NULL; + NMSettingBond *s_bond; + GError *error = NULL; + gboolean success; + const char **option; + + create_bond_connection (&con, &s_bond); + + for (option = options; option[0] && option[1]; option += 2) + g_assert (nm_setting_bond_add_option (s_bond, option[0], option[1])); + + if (expected_result) { + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + success = nm_setting_verify ((NMSetting *) s_bond, con, &error); + nmtst_assert_success (success, error); + } else { + nmtst_assert_connection_unnormalizable (con, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY); + } +} + +static void +test_bond_verify (void) +{ + test_verify_options (TRUE, + "mode", "3", + "arp_interval", "0"); + test_verify_options (FALSE, + /* arp_interval not supported in balance-alb mode */ + "mode", "balance-alb", + "arp_interval", "1", + "arp_ip_target", "1.2.3.4"); + test_verify_options (FALSE, + /* arp_ip_target requires arp_interval */ + "mode", "balance-rr", + "arp_ip_target", "1.2.3.4"); + test_verify_options (TRUE, + "mode", "balance-rr", + "arp_interval", "1", + "arp_ip_target", "1.2.3.4"); + test_verify_options (FALSE, + /* num_grat_arp, num_unsol_na cannot be different */ + "mode", "balance-rr", + "num_grat_arp", "3", + "num_unsol_na", "4"); + test_verify_options (TRUE, + "mode", "balance-rr", + "num_grat_arp", "5", + "num_unsol_na", "5"); + test_verify_options (TRUE, + "mode", "active-backup", + "primary", "eth0"); + test_verify_options (FALSE, + /* primary requires mode=active-backup */ + "mode", "802.3ad", + "primary", "eth0"); + test_verify_options (TRUE, + "mode", "802.3ad", + "lacp_rate", "fast"); + test_verify_options (FALSE, + /* lacp_rate=fast requires mode=802.3ad */ + "mode", "balance-rr", + "lacp_rate", "fast"); + test_verify_options (TRUE, + "mode", "802.3ad", + "ad_actor_system", "ae:00:11:33:44:55"); +} + +static void +test_bond_compare_options (gboolean exp_res, const char **opts1, const char **opts2) +{ + gs_unref_object NMSettingBond *s_bond1 = NULL, *s_bond2 = NULL; + const char **p; + + s_bond1 = (NMSettingBond *) nm_setting_bond_new (); + g_assert (s_bond1); + s_bond2 = (NMSettingBond *) nm_setting_bond_new (); + g_assert (s_bond2); + + for (p = opts1; p[0] && p[1]; p += 2) + g_assert (nm_setting_bond_add_option (s_bond1, p[0], p[1])); + + for (p = opts2; p[0] && p[1]; p += 2) + g_assert (nm_setting_bond_add_option (s_bond2, p[0], p[1])); + + g_assert_cmpint (nm_setting_compare ((NMSetting *) s_bond1, + (NMSetting *) s_bond2, + NM_SETTING_COMPARE_FLAG_EXACT), + ==, + exp_res); +} + +static void +test_bond_compare (void) +{ + test_bond_compare_options (TRUE, + ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), + ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL })); + test_bond_compare_options (FALSE, + ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), + ((const char *[]){ "mode", "balance-rr", "miimon", "2", NULL })); + + /* ignore default values */ + test_bond_compare_options (TRUE, + ((const char *[]){ "miimon", "1", NULL }), + ((const char *[]){ "miimon", "1", "updelay", "0", NULL })); + + /* special handling of num_grat_arp, num_unsol_na */ + test_bond_compare_options (FALSE, + ((const char *[]){ "num_grat_arp", "2", NULL }), + ((const char *[]){ "num_grat_arp", "1", NULL })); + test_bond_compare_options (TRUE, + ((const char *[]){ "num_grat_arp", "3", NULL }), + ((const char *[]){ "num_unsol_na", "3", NULL })); + test_bond_compare_options (TRUE, + ((const char *[]){ "num_grat_arp", "4", NULL }), + ((const char *[]){ "num_unsol_na", "4", "num_grat_arp", "4", NULL })); +} + +static void +test_bond_normalize_options (const char **opts1, const char **opts2) +{ + gs_unref_object NMConnection *con = NULL; + NMSettingBond *s_bond; + GError *error = NULL; + gboolean success; + const char **p; + int num = 0; + + create_bond_connection (&con, &s_bond); + + for (p = opts1; p[0] && p[1]; p += 2) + g_assert (nm_setting_bond_add_option (s_bond, p[0], p[1])); + + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + success = nm_setting_verify ((NMSetting *) s_bond, con, &error); + nmtst_assert_success (success, error); + + for (p = opts2; p[0] && p[1]; p += 2) { + g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, p[0]), ==, p[1]); + num++; + } + + g_assert_cmpint (num, ==, nm_setting_bond_get_num_options (s_bond)); +} + +static void +test_bond_normalize (void) +{ + test_bond_normalize_options ( + ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL }), + ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "1", "miimon", "1", NULL }), + ((const char *[]){ "mode", "active-backup", "miimon", "1", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "balance-alb", "tlb_dynamic_lb", "1", NULL }), + ((const char *[]){ "mode", "balance-alb", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL }), + ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "balance-rr", "ad_actor_sys_prio", "4", "packets_per_slave", "3", NULL }), + ((const char *[]){ "mode", "balance-rr", "packets_per_slave", "3", NULL })); +} + +/*****************************************************************************/ + +#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ + NM_SETTING_DCB_FLAG_ADVERTISE | \ + NM_SETTING_DCB_FLAG_WILLING) + + +static void +test_dcb_flags_valid (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0); + + g_object_set (G_OBJECT (s_dcb), + NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL, + NULL); + /* Priority Group Bandwidth must total 100% */ + for (i = 0; i < 7; i++) + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); + + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_no_error (error); + g_assert (success); + + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL); +} + +#define TEST_FLAG(p, f, v) \ +{ \ + /* GObject property min/max should ensure the property does not get set to \ + * the invalid value, so we ensure the value we just tried to set is 0 and \ + * that verify is successful since the property never got set. \ + */ \ + g_object_set (G_OBJECT (s_dcb), p, v, NULL); \ + g_assert_cmpint (f (s_dcb), ==, 0); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ +} + +static void +test_dcb_flags_invalid (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1); + g_test_assert_expected_messages (); +} + +#define TEST_APP_PRIORITY(lcprop, ucprop, v) \ +{ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ + \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \ + g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \ + \ + /* Assert that the setting is invalid while the app is disabled unless v is default */ \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + if (v >= 0) { \ + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ + g_assert (success == FALSE); \ + } else { \ + g_assert_no_error (error); \ + g_assert (success); \ + } \ + g_clear_error (&error); \ + \ + /* Set the enable flag and re-verify, this time it should be valid */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ + \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \ +} + +static void +test_dcb_app_priorities (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + /* Defaults */ + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1); + g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1); + + TEST_APP_PRIORITY (fcoe, FCOE, 6); + TEST_APP_PRIORITY (iscsi, ISCSI, 5); + TEST_APP_PRIORITY (fip, FIP, 4); + + TEST_APP_PRIORITY (fcoe, FCOE, -1); + TEST_APP_PRIORITY (iscsi, ISCSI, -1); + TEST_APP_PRIORITY (fip, FIP, -1); +} + +#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \ +{ \ + /* Assert that setting the value gets the same value back out */ \ + nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \ + g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \ + \ + if (verify) { \ + if (val != 0) { \ + /* Assert that verify fails because the flags do not include 'enabled' \ + * and a value has been set. \ + */ \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ + g_assert (success == FALSE); \ + g_clear_error (&error); \ + } \ + \ + /* Assert that adding the 'enabled' flag verifies the setting */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ + } \ + \ + /* Reset everything */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ + nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \ +} + +/* If Priority Groups are enabled, PG bandwidth must equal 100% */ +#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \ +{ \ + guint x; \ + for (x = 0; x < 7; x++) \ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \ +} + +static void +test_dcb_priorities_valid (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + for (i = 0; i < 8; i++) + TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE); + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE); + } + + /* Clear PG bandwidth from earlier tests */ + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0); + + /* Priority Group Bandwidth must add up to 100% if enabled, which requires + * some dancing for verifying individual values here. + */ + for (i = 0; i < 8; i++) { + guint other = 7 - (i % 8); + + /* Set another priority group to the remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i); + TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE); + + /* Set another priority group to the remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i)); + TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE); + + /* Clear remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0); + } + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE); + } + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) + TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE); + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE); + } +} + +static void +test_dcb_bandwidth_sums (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + /* Assert that setting the value gets the same value back out */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); + + /* Assert verify success when sums total 100% */ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_no_error (error); + g_assert (success); + + /* Assert verify fails when sums do not total 100% */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20); + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + g_assert (success == FALSE); + g_clear_error (&error); +} + +/*****************************************************************************/ + +static void +_test_team_config_sync (const char *team_config, + int notify_peer_count, + int notify_peers_interval, + int mcast_rejoin_count, + int mcast_rejoin_interval, + char *runner, + char *runner_hwaddr_policy, /* activebackup */ + GPtrArray *runner_tx_hash, /* lacp, loadbalance */ + char *runner_tx_balancer, /* lacp, loadbalance */ + int runner_tx_balancer_interval, /* lacp, loadbalance */ + gboolean runner_active, /* lacp */ + gboolean runner_fast_rate, /* lacp */ + int runner_sys_prio, /* lacp */ + int runner_min_ports, /* lacp */ + char *runner_agg_select_policy, /* lacp */ + GPtrArray *link_watchers) +{ + gs_unref_object NMSettingTeam *s_team = NULL; + guint i, j; + gboolean found; + + s_team = (NMSettingTeam *) nm_setting_team_new (); + g_assert (s_team); + + g_object_set (s_team, NM_SETTING_TEAM_CONFIG, team_config, NULL); + g_assert (nm_setting_team_get_notify_peers_count (s_team) == notify_peer_count); + g_assert (nm_setting_team_get_notify_peers_interval (s_team) == notify_peers_interval); + g_assert (nm_setting_team_get_mcast_rejoin_count (s_team) == mcast_rejoin_count); + g_assert (nm_setting_team_get_mcast_rejoin_interval (s_team) == mcast_rejoin_interval); + g_assert (nm_setting_team_get_runner_tx_balancer_interval (s_team) == runner_tx_balancer_interval); + g_assert (nm_setting_team_get_runner_active (s_team) == runner_active); + g_assert (nm_setting_team_get_runner_fast_rate (s_team) == runner_fast_rate); + g_assert (nm_setting_team_get_runner_sys_prio (s_team) == runner_sys_prio); + g_assert (nm_setting_team_get_runner_min_ports (s_team) == runner_min_ports); + g_assert (nm_streq0 (nm_setting_team_get_runner (s_team), runner)); + g_assert (nm_streq0 (nm_setting_team_get_runner_hwaddr_policy (s_team), runner_hwaddr_policy)); + g_assert (nm_streq0 (nm_setting_team_get_runner_tx_balancer (s_team), runner_tx_balancer)); + g_assert (nm_streq0 (nm_setting_team_get_runner_agg_select_policy (s_team), runner_agg_select_policy)); + + if (runner_tx_hash) { + g_assert (runner_tx_hash->len == nm_setting_team_get_num_runner_tx_hash (s_team)); + for (i = 0; i < runner_tx_hash->len; i++) { + found = FALSE; + for (j = 0; j < nm_setting_team_get_num_runner_tx_hash (s_team); j++) { + if (nm_streq0 (nm_setting_team_get_runner_tx_hash (s_team, j), + runner_tx_hash->pdata[i])) { + found = TRUE; + break; + } + } + g_assert (found); + } + } + + if (link_watchers) { + g_assert (link_watchers->len == nm_setting_team_get_num_link_watchers (s_team)); + for (i = 0; i < link_watchers->len; i++) { + found = FALSE; + for (j = 0; j < nm_setting_team_get_num_link_watchers (s_team); j++) { + if (nm_team_link_watcher_equal (link_watchers->pdata[i], + nm_setting_team_get_link_watcher (s_team, j))) { + found = TRUE; + break; + } + } + g_assert (found); + } + } + + g_assert (nm_setting_verify ((NMSetting *) s_team, NULL, NULL)); +} + + +static void +test_runner_roundrobin_sync_from_config (void) +{ + _test_team_config_sync ("", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_ROUNDROBIN, + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_broadcast_sync_from_config (void) +{ + _test_team_config_sync ("{\"runner\": {\"name\": \"broadcast\"}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_BROADCAST, + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_activebackup_sync_from_config (void) +{ + _test_team_config_sync ("{\"runner\": {\"name\": \"activebackup\"}}", + NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT, 0, + NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT, 0, + NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP, + NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_loadbalance_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *tx_hash = NULL; + + tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + g_ptr_array_add (tx_hash, g_strdup ("eth")); + g_ptr_array_add (tx_hash, g_strdup ("ipv4")); + g_ptr_array_add (tx_hash, g_strdup ("ipv6")); + + _test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\"}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + FALSE, FALSE, -1, -1, NULL, + NULL); + + _test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\", " + "\"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"]}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + FALSE, FALSE, -1, -1, NULL, + NULL); + + _test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"], " + "\"tx_balancer\": {\"name\": \"basic\", \"balancing_interval\": 30}}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NULL, + tx_hash, "basic", 30, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_lacp_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *tx_hash = NULL; + + tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + g_ptr_array_add (tx_hash, g_strdup ("eth")); + g_ptr_array_add (tx_hash, g_strdup ("ipv4")); + g_ptr_array_add (tx_hash, g_strdup ("ipv6")); + + _test_team_config_sync ("{\"runner\": {\"name\": \"lacp\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"]}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LACP, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + TRUE, FALSE, NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT, 0, + NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT, + NULL); + + _test_team_config_sync ("{\"runner\": {\"name\": \"lacp\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"], " + "\"active\": false, \"fast_rate\": true, \"sys_prio\": 10, \"min_ports\": 5, " + "\"agg_select_policy\": \"port_config\"}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LACP, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + FALSE, TRUE, 10, 5, "port_config", + NULL); +} + +static void +test_watcher_ethtool_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_ethtool (0, 0, NULL)); + _test_team_config_sync ("{\"link_watch\": {\"name\": \"ethtool\"}}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +static void +test_watcher_nsna_ping_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_nsna_ping (0, 0, 3, "target.host", NULL)); + _test_team_config_sync ("{\"link_watch\": {\"name\": \"nsna_ping\", \"target_host\": \"target.host\"}}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +static void +test_watcher_arp_ping_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (0, 0, 3, "target.host", "source.host", 0, NULL)); + _test_team_config_sync ("{\"link_watch\": {\"name\": \"arp_ping\", \"target_host\": \"target.host\", " + "\"source_host\": \"source.host\"}}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +static void +test_multiple_watchers_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_ethtool (2, 4, NULL)); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_nsna_ping (3, 6, 9, "target.host", NULL)); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (5, 10, 15, "target.host", "source.host", + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE + | NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE + | NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS, + NULL)); + _test_team_config_sync ("{\"link_watch\": [" + "{\"name\": \"ethtool\", \"delay_up\": 2, \"delay_down\": 4}, " + "{\"name\": \"arp_ping\", \"init_wait\": 5, \"interval\": 10, \"missed_max\": 15, " + "\"target_host\": \"target.host\", \"source_host\": \"source.host\", " + "\"validate_active\": true, \"validate_inactive\": true, \"send_always\": true}, " + "{\"name\": \"nsna_ping\", \"init_wait\": 3, \"interval\": 6, \"missed_max\": 9, " + "\"target_host\": \"target.host\"}]}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +/*****************************************************************************/ + +static void +_test_team_port_config_sync (const char *team_port_config, + int queue_id, + int prio, + gboolean sticky, + int lacp_prio, + int lacp_key, + GPtrArray *link_watchers) +{ + NMSettingTeamPort *s_team_port; + guint i, j; + gboolean found; + + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); + g_assert (s_team_port); + + g_object_set (s_team_port, NM_SETTING_TEAM_CONFIG, team_port_config, NULL); + g_assert (nm_setting_team_port_get_queue_id (s_team_port) == queue_id); + g_assert (nm_setting_team_port_get_prio (s_team_port) == prio); + g_assert (nm_setting_team_port_get_sticky (s_team_port) == sticky); + g_assert (nm_setting_team_port_get_lacp_prio (s_team_port) == lacp_prio); + g_assert (nm_setting_team_port_get_lacp_key (s_team_port) == lacp_key); + + if (link_watchers) { + g_assert (link_watchers->len == nm_setting_team_port_get_num_link_watchers (s_team_port)); + for (i = 0; i < link_watchers->len; i++) { + found = FALSE; + for (j = 0; j < nm_setting_team_port_get_num_link_watchers (s_team_port); j++) { + if (nm_team_link_watcher_equal (link_watchers->pdata[i], + nm_setting_team_port_get_link_watcher (s_team_port, + j))) { + found = TRUE; + break; + } + } + g_assert (found); + } + } + + g_assert (nm_setting_verify ((NMSetting *) s_team_port, NULL, NULL)); +} + + +static void +test_team_port_default (void) +{ + _test_team_port_config_sync ("", -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_queue_id (void) +{ + _test_team_port_config_sync ("{\"queue_id\": 3}", + 3, 0, FALSE, 255, 0, NULL); + _test_team_port_config_sync ("{\"queue_id\": 0}", + 0, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_prio (void) +{ + _test_team_port_config_sync ("{\"prio\": 6}", + -1, 6, FALSE, 255, 0, NULL); + _test_team_port_config_sync ("{\"prio\": 0}", + -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_sticky (void) +{ + _test_team_port_config_sync ("{\"sticky\": true}", + -1, 0, TRUE, 255, 0, NULL); + _test_team_port_config_sync ("{\"sticky\": false}", + -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_lacp_prio (void) +{ + _test_team_port_config_sync ("{\"lacp_prio\": 9}", + -1, 0, FALSE, 9, 0, NULL); + _test_team_port_config_sync ("{\"lacp_prio\": 0}", + -1, 0, FALSE, 0, 0, NULL); +} + +static void +test_team_port_lacp_key (void) +{ + _test_team_port_config_sync ("{\"lacp_key\": 12}", + -1, 0, FALSE, 255, 12, NULL); + _test_team_port_config_sync ("{\"lacp_key\": 0}", + -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_full_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (0, 3, 3, "1.2.3.2", "1.2.3.1", + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE, + NULL)); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (1, 1, 0, "1.2.3.4", "1.2.3.1", + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS, + NULL)); + + _test_team_port_config_sync ("{\"queue_id\": 10, \"prio\": 20, \"sticky\": true, \"lacp_prio\": 30, " + "\"lacp_key\": 40, \"link_watch\": [" + "{\"name\": \"arp_ping\", \"interval\": 3, \"target_host\": \"1.2.3.2\", " + "\"source_host\": \"1.2.3.1\", \"validate_inactive\": true}, " + "{\"name\": \"arp_ping\", \"init_wait\": 1, \"interval\": 1, " + "\"target_host\": \"1.2.3.4\", \"source_host\": \"1.2.3.1\", " + "\"send_always\": true}]}", + 10, 20, true, 30, 40, NULL); +} + +/*****************************************************************************/ + + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + nmtst_init (&argc, &argv, TRUE); + + g_test_add_data_func ("/libnm/setting-8021x/key-and-cert", + "test_key_and_cert.pem, test", + test_8021x); + g_test_add_data_func ("/libnm/setting-8021x/key-only", + "test-key-only.pem, test", + test_8021x); + g_test_add_data_func ("/libnm/setting-8021x/pkcs8-enc-key", + "pkcs8-enc-key.pem, 1234567890", + test_8021x); + g_test_add_data_func ("/libnm/setting-8021x/pkcs12", + "test-cert.p12, test", + test_8021x); + + g_test_add_func ("/libnm/settings/bond/verify", test_bond_verify); + g_test_add_func ("/libnm/settings/bond/compare", test_bond_compare); + g_test_add_func ("/libnm/settings/bond/normalize", test_bond_normalize); + + g_test_add_func ("/libnm/settings/dcb/flags-valid", test_dcb_flags_valid); + g_test_add_func ("/libnm/settings/dcb/flags-invalid", test_dcb_flags_invalid); + g_test_add_func ("/libnm/settings/dcb/app-priorities", test_dcb_app_priorities); + g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid); + g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums); + +#if WITH_TEAM + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin", + test_runner_roundrobin_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_broadcast", + test_runner_broadcast_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_activebackup", + test_runner_activebackup_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_loadbalance", + test_runner_loadbalance_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_lacp", + test_runner_lacp_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_ethtool", + test_watcher_ethtool_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_nsna_ping", + test_watcher_nsna_ping_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_arp_ping", + test_watcher_arp_ping_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_all", + test_multiple_watchers_sync_from_config); + + g_test_add_func ("/libnm/settings/team-port/sync_from_config_defaults", test_team_port_default); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_queue_id", test_team_port_queue_id); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_prio", test_team_port_prio); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_sticky", test_team_port_sticky); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_prio", test_team_port_lacp_prio); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_key", test_team_port_lacp_key); + g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config); + +#endif + + return g_test_run (); +} diff --git a/libnm/generate-setting-docs.py b/libnm/generate-setting-docs.py index aa0c96ebb2..50ae9d4682 100755 --- a/libnm/generate-setting-docs.py +++ b/libnm/generate-setting-docs.py @@ -54,6 +54,7 @@ dbus_type_name_map = { 'ay': 'byte array', 'a{ss}': 'dict of string to string', 'a{sv}': 'vardict', + 'aa{sv}': 'array of vardict', 'aau': 'array of array of uint32', 'aay': 'array of byte array', 'a(ayuay)': 'array of legacy IPv6 address struct', diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 7d6ce75195..e607a5dd8d 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1241,11 +1241,15 @@ global: nm_client_checkpoint_rollback_async; nm_client_checkpoint_rollback_finish; nm_client_get_checkpoints; + nm_setting_team_add_link_watcher; nm_setting_team_add_runner_tx_hash; + nm_setting_team_clear_link_watchers; + nm_setting_team_get_link_watcher; nm_setting_team_get_mcast_rejoin_count; nm_setting_team_get_mcast_rejoin_interval; nm_setting_team_get_notify_peers_count; nm_setting_team_get_notify_peers_interval; + nm_setting_team_get_num_link_watchers; nm_setting_team_get_num_runner_tx_hash; nm_setting_team_get_runner; nm_setting_team_get_runner_hwaddr_policy; @@ -1257,13 +1261,39 @@ global: nm_setting_team_get_runner_sys_prio; nm_setting_team_get_runner_min_ports; nm_setting_team_get_runner_agg_select_policy; + nm_setting_team_port_add_link_watcher; + nm_setting_team_port_clear_link_watchers; nm_setting_team_port_get_queue_id; nm_setting_team_port_get_prio; nm_setting_team_port_get_sticky; nm_setting_team_port_get_lacp_prio; nm_setting_team_port_get_lacp_key; + nm_setting_team_port_get_link_watcher; + nm_setting_team_port_get_num_link_watchers; + nm_setting_team_port_remove_link_watcher; + nm_setting_team_port_remove_link_watcher_by_value; + nm_setting_team_remove_link_watcher; + nm_setting_team_remove_link_watcher_by_value; nm_setting_team_remove_runner_tx_hash; nm_setting_team_remove_runner_tx_hash_by_value; nm_setting_vpn_get_data_keys; nm_setting_vpn_get_secret_keys; + nm_team_link_watcher_arp_ping_flags_get_type; + nm_team_link_watcher_dup; + nm_team_link_watcher_equal; + nm_team_link_watcher_get_delay_down; + nm_team_link_watcher_get_delay_up; + nm_team_link_watcher_get_flags; + nm_team_link_watcher_get_init_wait; + nm_team_link_watcher_get_interval; + nm_team_link_watcher_get_missed_max; + nm_team_link_watcher_get_name; + nm_team_link_watcher_get_source_host; + nm_team_link_watcher_get_target_host; + nm_team_link_watcher_get_type; + nm_team_link_watcher_new_arp_ping; + nm_team_link_watcher_new_ethtool; + nm_team_link_watcher_new_nsna_ping; + nm_team_link_watcher_ref; + nm_team_link_watcher_unref; } libnm_1_10_0; diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected index 460278e1eb..2df1fbb38b 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected @@ -1,6 +1,6 @@ CONNECTED_MODE=no TYPE=InfiniBand -TEAM_PORT_CONFIG="{ \"inf1\": { \"prio\": -10, \"sticky\": true } }" +TEAM_PORT_CONFIG="{\"inf1\": {\"prio\": -10, \"sticky\": true}}" NAME="Test Write Team Infiniband Port" UUID=${UUID} DEVICE=inf1 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected index 0b1deb8077..ff55cefe7f 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected @@ -1,4 +1,4 @@ -TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }" +TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}" NAME="Test Write Team Port" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 index 7edc736af0..209447b844 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 @@ -2,5 +2,5 @@ DEVICE=team0 ONBOOT=no DEVICETYPE=Team BOOTPROTO=dhcp -TEAM_CONFIG="{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }" +TEAM_CONFIG="{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 index d01e37c552..26e448cc7e 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 @@ -1,5 +1,5 @@ DEVICE=team0 ONBOOT=no BOOTPROTO=dhcp -TEAM_CONFIG="{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }" +TEAM_CONFIG="{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 index 966bec6777..80355c2643 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 @@ -1,5 +1,5 @@ TYPE=Ethernet -TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }" +TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}" DEVICE=p4p1 TEAM_MASTER=team0 DEVICETYPE=TeamPort diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 index 992510ee0a..4284737a1c 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 @@ -1,4 +1,4 @@ TYPE=Ethernet -TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }" +TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}" DEVICE=p4p1 TEAM_MASTER=team0 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 6510136fc3..6bf2755613 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -8695,7 +8695,7 @@ test_read_team_master (gconstpointer user_data) NMConnection *connection; NMSettingConnection *s_con; NMSettingTeam *s_team; - const char *expected_config = "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"; + const char *expected_config = "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"; connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); @@ -8748,7 +8748,7 @@ test_write_team_master (void) NMSettingWired *s_wired; NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; - const char *expected_config = "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"; + const char *expected_config = "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"; shvarFile *f; connection = nm_simple_connection_new (); @@ -8817,7 +8817,7 @@ test_read_team_port (gconstpointer user_data) NMConnection *connection; NMSettingConnection *s_con; NMSettingTeamPort *s_team_port; - const char *expected_config = "{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"; + const char *expected_config = "{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"; connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); @@ -8842,7 +8842,7 @@ test_write_team_port (void) NMSettingConnection *s_con; NMSettingTeamPort *s_team_port; NMSettingWired *s_wired; - const char *expected_config = "{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"; + const char *expected_config = "{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"; shvarFile *f; connection = nm_simple_connection_new (); @@ -8897,7 +8897,7 @@ test_write_team_infiniband_port (void) NMSettingConnection *s_con; NMSettingTeamPort *s_team_port; NMSettingInfiniband *s_inf; - const char *expected_config = "{ \"inf1\": { \"prio\": -10, \"sticky\": true } }"; + const char *expected_config = "{\"inf1\": {\"prio\": -10, \"sticky\": true}}"; shvarFile *f; connection = nm_simple_connection_new (); @@ -9258,7 +9258,9 @@ test_svUnescape (void) V0 ("Bob outside LAN", NULL), V1 ("x", "x"), V1 ("'{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }'", - "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"), + "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"), + V1 ("'{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}'", + "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"), V1 ("x\"\"b", "xb"), V1 ("x\"c\"b", "xcb"), V1 ("\"c\"b", "cb"), |