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");