diff --git a/include/NetworkManager.h b/include/NetworkManager.h index 77360561d9..27c20f6000 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -51,6 +51,7 @@ #define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config" #define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband" #define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond" +#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team" #define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan" #define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge" #define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic" @@ -118,6 +119,7 @@ typedef enum { * @NM_DEVICE_TYPE_VLAN: an 802.1Q VLAN interface * @NM_DEVICE_TYPE_ADSL: ADSL modem * @NM_DEVICE_TYPE_BRIDGE: a bridge master interface + * @NM_DEVICE_TYPE_TEAM: a team master interface * * #NMDeviceType values indicate the type of hardware represented by * an #NMDevice. @@ -138,6 +140,7 @@ typedef enum { NM_DEVICE_TYPE_ADSL = 12, NM_DEVICE_TYPE_BRIDGE = 13, NM_DEVICE_TYPE_GENERIC = 14, + NM_DEVICE_TYPE_TEAM = 15, } NMDeviceType; /** diff --git a/introspection/Makefile.am b/introspection/Makefile.am index fc2d773e0b..1cbb17e060 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -13,6 +13,7 @@ EXTRA_DIST = \ nm-device-wimax.xml \ nm-device-infiniband.xml \ nm-device-bond.xml \ + nm-device-team.xml \ nm-device-bridge.xml \ nm-device-vlan.xml \ nm-device-generic.xml \ diff --git a/introspection/nm-device-team.xml b/introspection/nm-device-team.xml new file mode 100644 index 0000000000..baf39fa860 --- /dev/null +++ b/introspection/nm-device-team.xml @@ -0,0 +1,34 @@ + + + + + + + + Hardware address of the device. + + + + + + Indicates whether the physical carrier is found (e.g. whether a cable is plugged in or not). + + + + + + Array of object paths representing devices which are currently + slaved to this device. + + + + + + + A dictionary mapping property names to variant boxed values + + + + + + diff --git a/po/POTFILES.in b/po/POTFILES.in index 7de7f33e3a..0180e912a3 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -51,6 +51,7 @@ src/devices/nm-device-bt.c src/devices/nm-device-ethernet.c src/devices/nm-device-infiniband.c src/devices/nm-device-olpc-mesh.c +src/devices/nm-device-team.c src/devices/nm-device-vlan.c src/nm-manager.c src/nm-netlink-monitor.c diff --git a/src/Makefile.am b/src/Makefile.am index d67583367c..8370e5a068 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,6 +88,8 @@ nm_sources = \ devices/nm-device-olpc-mesh.c \ devices/nm-device-olpc-mesh.h \ devices/nm-device-private.h \ + devices/nm-device-team.c \ + devices/nm-device-team.h \ devices/nm-device-tun.c \ devices/nm-device-tun.h \ devices/nm-device-veth.c \ @@ -323,6 +325,7 @@ glue_sources = \ nm-device-macvlan-glue.h \ nm-device-modem-glue.h \ nm-device-olpc-mesh-glue.h \ + nm-device-team-glue.h \ nm-device-tun-glue.h \ nm-device-veth-glue.h \ nm-device-vlan-glue.h \ diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c new file mode 100644 index 0000000000..e7718a1466 --- /dev/null +++ b/src/devices/nm-device-team.c @@ -0,0 +1,352 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * Copyright (C) 2013 Jiri Pirko + * + * 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 of the License, 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. + */ + +#include "config.h" + +#include +#include + +#include + +#include "nm-device-team.h" +#include "nm-logging.h" +#include "nm-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-device-private.h" +#include "nm-platform.h" +#include "nm-dbus-glib-types.h" +#include "nm-dbus-manager.h" +#include "nm-enum-types.h" + +#include "nm-device-team-glue.h" + + +G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE) + +#define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate)) + +#define NM_TEAM_ERROR (nm_team_error_quark ()) + +typedef struct { + int dummy; +} NMDeviceTeamPrivate; + +enum { + PROP_0, + PROP_SLAVES, + + LAST_PROP +}; + +/******************************************************************/ + +static GQuark +nm_team_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-team-error"); + return quark; +} + +/******************************************************************/ + +static guint32 +get_generic_capabilities (NMDevice *dev) +{ + return NM_DEVICE_CAP_CARRIER_DETECT; +} + +static gboolean +is_available (NMDevice *dev) +{ + if (NM_DEVICE_GET_CLASS (dev)->is_up) + return NM_DEVICE_GET_CLASS (dev)->is_up (dev); + return FALSE; +} + +static gboolean +check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + const char *iface; + NMSettingTeam *s_team; + + if (!NM_DEVICE_CLASS (nm_device_team_parent_class)->check_connection_compatible (device, connection, error)) + return FALSE; + + s_team = nm_connection_get_setting_team (connection); + if (!s_team || !nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) { + g_set_error (error, NM_TEAM_ERROR, NM_TEAM_ERROR_CONNECTION_NOT_TEAM, + "The connection was not a team connection."); + return FALSE; + } + + /* Team connections must specify the virtual interface name */ + iface = nm_connection_get_virtual_iface_name (connection); + if (!iface || strcmp (nm_device_get_iface (device), iface)) { + g_set_error (error, NM_TEAM_ERROR, NM_TEAM_ERROR_CONNECTION_NOT_TEAM, + "The team connection virtual interface name did not match."); + return FALSE; + } + + /* FIXME: match team properties like mode, etc? */ + + return TRUE; +} + +static gboolean +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMSettingTeam *s_team, *tmp; + guint32 i = 0; + char *name; + const GSList *iter; + gboolean found; + + nm_utils_complete_generic (connection, + NM_SETTING_TEAM_SETTING_NAME, + existing_connections, + _("Team connection %d"), + NULL, + TRUE); + + s_team = nm_connection_get_setting_team (connection); + if (!s_team) { + s_team = (NMSettingTeam *) nm_setting_team_new (); + nm_connection_add_setting (connection, NM_SETTING (s_team)); + } + + /* Grab the first name that doesn't exist in either our connections + * or a device on the system. + */ + while (i < 500 && !nm_setting_team_get_interface_name (s_team)) { + name = g_strdup_printf ("team%u", i); + /* check interface names */ + if (!nm_platform_link_exists (name)) { + /* check existing team connections */ + for (iter = existing_connections, found = FALSE; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = iter->data; + + tmp = nm_connection_get_setting_team (candidate); + if (tmp && nm_connection_is_type (candidate, NM_SETTING_TEAM_SETTING_NAME)) { + if (g_strcmp0 (nm_setting_team_get_interface_name (tmp), name) == 0) { + found = TRUE; + break; + } + } + } + + if (!found) + g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_INTERFACE_NAME, name, NULL); + } + + g_free (name); + i++; + } + + return TRUE; +} + +static gboolean +match_l2_config (NMDevice *self, NMConnection *connection) +{ + /* FIXME */ + return TRUE; +} + +/******************************************************************/ + +static NMActStageReturn +act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMConnection *connection; + NMSettingTeam *s_team; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (dev, reason); + if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { + connection = nm_device_get_connection (dev); + g_assert (connection); + s_team = nm_connection_get_setting_team (connection); + g_assert (s_team); + } + return ret; +} + +static gboolean +enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) +{ + gboolean success, no_firmware = FALSE; + const char *iface = nm_device_get_ip_iface (device); + const char *slave_iface = nm_device_get_ip_iface (slave); + + nm_device_take_down (slave, TRUE); + + success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); + + nm_device_bring_up (slave, TRUE, &no_firmware); + + if (success) { + nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface); + g_object_notify (G_OBJECT (device), "slaves"); + } + + return success; +} + +static gboolean +release_slave (NMDevice *device, NMDevice *slave) +{ + gboolean success, no_firmware = FALSE; + + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); + + nm_log_info (LOGD_TEAM, "(%s): released team port %s (success %d)", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave), + success); + g_object_notify (G_OBJECT (device), "slaves"); + + /* Kernel team code "closes" the port when releasing it, (which clears + * IFF_UP), so we must bring it back up here to ensure carrier changes and + * other state is noticed by the now-released port. + */ + if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { + nm_log_warn (LOGD_TEAM, "(%s): released team port could not be brought up.", + nm_device_get_iface (slave)); + } + + return success; +} + +/******************************************************************/ + +NMDevice * +nm_device_team_new (const char *iface) +{ + g_return_val_if_fail (iface != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TEAM, + NM_DEVICE_IFACE, iface, + NM_DEVICE_DRIVER, "team", + NM_DEVICE_TYPE_DESC, "Team", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TEAM, + NM_DEVICE_IS_MASTER, TRUE, + NULL); +} + +static void +constructed (GObject *object) +{ + G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object); + + nm_log_dbg (LOGD_HW | LOGD_TEAM, "(%s): kernel ifindex %d", + nm_device_get_iface (NM_DEVICE (object)), + nm_device_get_ifindex (NM_DEVICE (object))); +} + +static void +nm_device_team_init (NMDeviceTeam * self) +{ +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GPtrArray *slaves; + GSList *list, *iter; + + switch (prop_id) { + break; + case PROP_SLAVES: + slaves = g_ptr_array_new (); + list = nm_device_master_get_slaves (NM_DEVICE (object)); + for (iter = list; iter; iter = iter->next) + g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data)))); + g_slist_free (list); + g_value_take_boxed (value, slaves); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_team_class_init (NMDeviceTeamClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceTeamPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->set_property = set_property; + + parent_class->get_generic_capabilities = get_generic_capabilities; + parent_class->is_available = is_available; + parent_class->check_connection_compatible = check_connection_compatible; + parent_class->complete_connection = complete_connection; + + parent_class->match_l2_config = match_l2_config; + + parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->enslave_slave = enslave_slave; + parent_class->release_slave = release_slave; + + /* properties */ + g_object_class_install_property + (object_class, PROP_SLAVES, + g_param_spec_boxed (NM_DEVICE_TEAM_SLAVES, + "Slaves", + "Slaves", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_team_object_info); + + dbus_g_error_domain_register (NM_TEAM_ERROR, NULL, NM_TYPE_TEAM_ERROR); +} diff --git a/src/devices/nm-device-team.h b/src/devices/nm-device-team.h new file mode 100644 index 0000000000..abeb7e9f4f --- /dev/null +++ b/src/devices/nm-device-team.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * Copyright (C) 2013 Jiri Pirko + * + * 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 of the License, 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. + */ + +#ifndef NM_DEVICE_TEAM_H +#define NM_DEVICE_TEAM_H + +#include + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_TEAM (nm_device_team_get_type ()) +#define NM_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeam)) +#define NM_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass)) +#define NM_IS_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_TEAM)) +#define NM_IS_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_TEAM)) +#define NM_DEVICE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass)) + +typedef enum { + NM_TEAM_ERROR_CONNECTION_NOT_TEAM = 0, /*< nick=ConnectionNotTeam >*/ + NM_TEAM_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ + NM_TEAM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ +} NMTeamError; + +#define NM_DEVICE_TEAM_SLAVES "slaves" + +typedef struct { + NMDevice parent; +} NMDeviceTeam; + +typedef struct { + NMDeviceClass parent; + +} NMDeviceTeamClass; + + +GType nm_device_team_get_type (void); + +NMDevice *nm_device_team_new (const char *iface); + +G_END_DECLS + +#endif /* NM_DEVICE_TEAM_H */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 51c3ab77c7..323f8c0532 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -280,7 +280,7 @@ typedef struct { /* allow autoconnect feature */ gboolean autoconnect; - /* master interface for bridge/bond slave */ + /* master interface for bridge/bond/team slave */ NMDevice * master; gboolean enslaved; @@ -806,16 +806,18 @@ nm_device_get_priority (NMDevice *dev) return 4; case NM_DEVICE_TYPE_BOND: return 5; - case NM_DEVICE_TYPE_VLAN: + case NM_DEVICE_TYPE_TEAM: return 6; - case NM_DEVICE_TYPE_MODEM: + case NM_DEVICE_TYPE_VLAN: return 7; - case NM_DEVICE_TYPE_BT: + case NM_DEVICE_TYPE_MODEM: return 8; - case NM_DEVICE_TYPE_WIFI: + case NM_DEVICE_TYPE_BT: return 9; - case NM_DEVICE_TYPE_OLPC_MESH: + case NM_DEVICE_TYPE_WIFI: return 10; + case NM_DEVICE_TYPE_OLPC_MESH: + return 11; default: return 20; } @@ -901,8 +903,8 @@ free_slave_info (SlaveInfo *info) * @slave: the slave device to enslave * @connection: the slave device's connection * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc) - * then this function enslaves @slave. + * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, + * etc) then this function enslaves @slave. * * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave * other devices. @@ -955,8 +957,8 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio * @slave: the slave device to release * @failed: %TRUE if the release was unexpected, ie the master failed * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc) - * then this function releases the previously enslaved @slave. + * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, + * etc) then this function releases the previously enslaved @slave. * * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave * other devices, or if @slave was never enslaved. @@ -1050,9 +1052,9 @@ carrier_changed (NMDevice *device, gboolean carrier) } if (nm_device_is_master (device)) { - /* Bridge/bond carrier does not affect its own activation, but - * when carrier comes on, if there are slaves waiting, it will - * restart them. + /* Bridge/bond/team carrier does not affect its own activation, + * but when carrier comes on, if there are slaves waiting, + * it will restart them. */ if (!carrier) return; @@ -1064,8 +1066,9 @@ carrier_changed (NMDevice *device, gboolean carrier) return; } else if (nm_device_get_enslaved (device) && !carrier) { - /* Slaves don't deactivate when they lose carrier; for bonds - * in particular that would be actively counterproductive. + /* Slaves don't deactivate when they lose carrier; for + * bonds/teams in particular that would be actively + * counterproductive. */ return; } @@ -1231,7 +1234,7 @@ slave_state_changed (NMDevice *slave, if (release) { nm_device_release_one_slave (self, slave, FALSE); - /* Bridge/bond interfaces are left up until manually deactivated */ + /* Bridge/bond/team interfaces are left up until manually deactivated */ if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) { nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated", nm_device_get_iface (self)); @@ -1244,8 +1247,8 @@ slave_state_changed (NMDevice *slave, * @dev: the master device * @slave: the slave device to enslave * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc) - * then this function adds @slave to the slave list for later enslavement. + * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, + * etc) then this function adds @slave to the slave list for later enslavement. * * Returns: %TRUE on success, %FALSE on failure */ @@ -1316,7 +1319,7 @@ nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex) * nm_device_is_master: * @dev: the device * - * Returns: whether @dev can enslave other devices (eg, bridge or bond) + * Returns: whether @dev can enslave other devices (eg, bridge or bond or team) */ gboolean nm_device_is_master (NMDevice *dev) @@ -1405,7 +1408,7 @@ nm_device_slave_notify_enslaved (NMDevice *dev, * @device: the #NMDevice * * Returns: %TRUE if the device is enslaved to a master device (eg bridge or - * bond), %FALSE if not + * bond or team), %FALSE if not */ gboolean nm_device_get_enslaved (NMDevice *device) diff --git a/src/logging/nm-logging.c b/src/logging/nm-logging.c index 05b021b087..e7cfd1d5df 100644 --- a/src/logging/nm-logging.c +++ b/src/logging/nm-logging.c @@ -49,7 +49,7 @@ nm_log_handler (const gchar *log_domain, LOGD_SUPPLICANT | LOGD_AGENTS | LOGD_SETTINGS | LOGD_SUSPEND | \ LOGD_CORE | LOGD_DEVICE | LOGD_OLPC_MESH | LOGD_WIMAX | \ LOGD_INFINIBAND | LOGD_FIREWALL | LOGD_ADSL | LOGD_BOND | \ - LOGD_VLAN | LOGD_BRIDGE | LOGD_DBUS_PROPS) + LOGD_VLAN | LOGD_BRIDGE | LOGD_DBUS_PROPS | LOGD_TEAM) #define LOGD_DEFAULT (LOGD_ALL & ~(LOGD_WIFI_SCAN | LOGD_DBUS_PROPS)) @@ -103,6 +103,7 @@ static const LogDesc domain_descs[] = { { LOGD_VLAN, "VLAN" }, { LOGD_BRIDGE, "BRIDGE" }, { LOGD_DBUS_PROPS,"DBUS_PROPS" }, + { LOGD_TEAM, "TEAM" }, { 0, NULL } }; diff --git a/src/logging/nm-logging.h b/src/logging/nm-logging.h index 147445bcee..d2ef606ebf 100644 --- a/src/logging/nm-logging.h +++ b/src/logging/nm-logging.h @@ -59,6 +59,7 @@ enum { LOGD_VLAN = 0x10000000, LOGD_BRIDGE = 0x20000000, LOGD_DBUS_PROPS = 0x40000000, + LOGD_TEAM = 0x80000000, }; #define LOGD_DHCP (LOGD_DHCP4 | LOGD_DHCP6) diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index 359a68e54e..bd375e41bc 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -360,8 +360,8 @@ device_state_changed (NMDevice *device, GParamSpec *pspec, NMActRequest *self) * existing connection made before this instance of NM started * @device: the device/interface to configure according to @connection * @master: if the activation depends on another device (ie, bond or bridge - * master to which this device will be enslaved) pass the #NMDevice that this - * activation request be enslaved to + * or team master to which this device will be enslaved) pass the #NMDevice + * that this activation request be enslaved to * * Begins activation of @device using the given @connection and other details. * diff --git a/src/nm-manager.c b/src/nm-manager.c index bc7cdbf555..35f17ff161 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -48,6 +48,7 @@ #include "nm-device-modem.h" #include "nm-device-infiniband.h" #include "nm-device-bond.h" +#include "nm-device-team.h" #include "nm-device-bridge.h" #include "nm-device-vlan.h" #include "nm-device-adsl.h" @@ -1102,6 +1103,9 @@ get_virtual_iface_name (NMManager *self, if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) return g_strdup (nm_connection_get_virtual_iface_name (connection)); + if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) + return g_strdup (nm_connection_get_virtual_iface_name (connection)); + if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) return g_strdup (nm_connection_get_virtual_iface_name (connection)); @@ -1172,6 +1176,7 @@ static gboolean connection_needs_virtual_device (NMConnection *connection) { if ( nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) return TRUE; @@ -1328,6 +1333,14 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) } device = nm_device_bond_new (iface); + } else if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) { + if (!nm_platform_team_add (iface)) { + nm_log_warn (LOGD_DEVICE, "(%s): failed to add team master interface for '%s'", + iface, nm_connection_get_id (connection)); + goto out; + } + + device = nm_device_team_new (iface); } else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) { gboolean result; @@ -2305,6 +2318,9 @@ platform_link_added_cb (NMPlatform *platform, case NM_LINK_TYPE_BOND: device = nm_device_bond_new (link->name); break; + case NM_LINK_TYPE_TEAM: + device = nm_device_team_new (link->name); + break; case NM_LINK_TYPE_BRIDGE: /* FIXME: always create device when we handle bridges non-destructively */ if (bridge_created_by_nm (self, link->name)) @@ -2700,7 +2716,9 @@ ensure_master_active_connection (NMManager *self, for (iter = connections; iter; iter = g_slist_next (iter)) { NMConnection *candidate = NM_CONNECTION (iter->data); - /* Ensure eg bond slave and the candidate master is a bond master */ + /* Ensure eg bond/team slave and the candidate master is a + * bond/team master + */ if (!is_compatible_with_slave (candidate, connection)) continue; @@ -2915,10 +2933,11 @@ nm_manager_activate_connection (NMManager *manager, } } } else { - /* Virtual connections (VLAN, bond, etc) may not specify a device - * path because the device may not be created yet, or it be given - * by the connection's properties instead. Find the device the - * connection refers to, or create it if needed. + /* Virtual connections (VLAN, bond, team, etc) may not specify + * a device path because the device may not be created yet, + * or it be given by the connection's properties instead. + * Find the device the connection refers to, or create it + * if needed. */ if (!connection_needs_virtual_device (connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, @@ -2996,7 +3015,9 @@ nm_manager_activate_connection (NMManager *manager, nm_device_get_ip_iface (master_device)); } - /* Ensure eg bond slave and the candidate master is a bond master */ + /* Ensure eg bond/team slave and the candidate master is + * a bond/team master + */ if (master_connection && !is_compatible_with_slave (master_connection, connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, "The master connection was not compatible");