summaryrefslogtreecommitdiff
path: root/src/devices/openvswitch/nm-device-openvswitch.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/openvswitch/nm-device-openvswitch.c')
-rw-r--r--src/devices/openvswitch/nm-device-openvswitch.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/devices/openvswitch/nm-device-openvswitch.c b/src/devices/openvswitch/nm-device-openvswitch.c
new file mode 100644
index 0000000000..761a638603
--- /dev/null
+++ b/src/devices/openvswitch/nm-device-openvswitch.c
@@ -0,0 +1,349 @@
+/* NetworkManager -- Network link manager
+ *
+ * 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.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-openvswitch.h"
+#include "nm-ovsdb.h"
+
+#include "devices/nm-device-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-port.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.Openvswitch.h"
+
+#include "devices/nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceOpenvswitch);
+
+/*****************************************************************************/
+
+struct _NMDeviceOpenvswitch {
+ NMDevice parent;
+};
+
+struct _NMDeviceOpenvswitchClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOpenvswitch, nm_device_openvswitch, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static void
+link_changed (NMDevice *device, const NMPlatformLink *pllink)
+{
+ NMDeviceOpenvswitch *self = NM_DEVICE_OPENVSWITCH (device);
+
+ NM_DEVICE_CLASS (nm_device_openvswitch_parent_class)->link_changed (device, pllink);
+
+ if (pllink && nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG) {
+ _LOGD (LOGD_DEVICE, "the link appeared, continuing activation");
+ nm_device_activate_schedule_stage2_device_config (device);
+ }
+}
+
+static void
+add_br_cb (GError *error, gpointer user_data)
+{
+ NMDeviceOpenvswitch *self = user_data;
+
+ if (error) {
+ _LOGW (LOGD_DEVICE, "%s", error->message);
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_UNKNOWN);
+ }
+
+ g_object_unref (self);
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+ NMConnection *connection,
+ NMDevice *parent,
+ const NMPlatformLink **out_plink,
+ GError **error)
+{
+ const char *connection_type;
+
+ connection_type = nm_connection_get_connection_type (connection);
+ g_return_val_if_fail (connection_type, FALSE);
+
+ if (strcmp (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0) {
+ nm_ovsdb_transact (nm_ovsdb_get (), NM_OVSDB_ADD_BR,
+ nm_device_get_iface (device), connection, NULL,
+ add_br_cb, g_object_ref (device));
+
+ /* We don't have a plink yet, since the device is eventually instantiated
+ * by ovs-vswitchd asynchronously. Manager knows and manager is fine with that. */
+ } else if (strcmp (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) {
+ /* This doesn't really exist, not even in the ovsdb, until an interface is
+ * enslaved. */
+ } else {
+ g_return_val_if_reached (FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+del_br_cb (GError *error, gpointer user_data)
+{
+ NMDeviceOpenvswitch *self = user_data;
+
+ if (error) {
+ _LOGW (LOGD_DEVICE, "%s", error->message);
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_UNKNOWN);
+ }
+
+ g_object_unref (self);
+}
+
+static gboolean
+unrealize (NMDevice *device, GError **error)
+{
+ nm_ovsdb_transact (nm_ovsdb_get (), NM_OVSDB_DEL_BR,
+ nm_device_get_iface (device), NULL, NULL,
+ del_br_cb, g_object_ref (device));
+
+ return TRUE;
+}
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *device)
+{
+ return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ const char *connection_type;
+
+ if (!NM_DEVICE_CLASS (nm_device_openvswitch_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+ connection_type = nm_setting_connection_get_connection_type (s_con);
+ if (!connection_type)
+ return FALSE;
+
+ if (strcmp (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0)
+ return TRUE;
+ if (strcmp (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+check_slave_connection_compatible (NMDevice *device, NMConnection *slave)
+{
+ NMSettingConnection *s_con;
+ const char *slave_type;
+
+ s_con = nm_connection_get_setting_connection (slave);
+ g_assert (s_con);
+ slave_type = nm_setting_connection_get_slave_type (s_con);
+ if (!slave_type)
+ return FALSE;
+
+ if (strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0)
+ return TRUE;
+ if (strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+ NMDeviceOpenvswitch *self = NM_DEVICE_OPENVSWITCH (device);
+ NMConnection *applied_connection;
+
+ applied_connection = nm_device_get_applied_connection (device);
+ if ( applied_connection
+ && strcmp (nm_connection_get_connection_type (applied_connection),
+ NM_SETTING_OVS_PORT_SETTING_NAME) == 0) {
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+ }
+
+ if (nm_device_get_ifindex (device)) {
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+ } else {
+ _LOGD (LOGD_DEVICE, "the link is not there, waiting for it to appear");
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ }
+}
+
+static void
+add_iface_cb (GError *error, gpointer user_data)
+{
+ NMDevice *slave = user_data;
+
+ if (error) {
+ nm_log_warn (LOGD_DEVICE, "device %s could not be added to a ovs port: %s",
+ nm_device_get_iface (slave), error->message);
+ nm_device_state_changed (slave,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_UNKNOWN);
+ }
+
+ g_object_unref (slave);
+}
+
+static gboolean
+_get_bridge_port (NMDevice *device, NMDevice *slave, NMConnection *connection,
+ NMDevice **bridge, NMDevice **port)
+{
+ NMConnection *applied_connection;
+ const char *device_type;
+ const char *slave_type = NULL;
+
+ if (!connection)
+ connection = nm_device_get_applied_connection (slave);
+ if (connection)
+ slave_type = nm_connection_get_connection_type (connection);
+
+ applied_connection = nm_device_get_applied_connection (device);
+ if (!applied_connection)
+ return FALSE;
+ device_type = nm_connection_get_connection_type (applied_connection);
+
+ /* Do nothing if we're just enslaving an empty port to a bridge. */
+ if (g_strcmp0 (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) {
+ *bridge = NULL;
+ *port = NULL;
+ return g_strcmp0 (device_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0;
+ }
+
+ if (g_strcmp0 (device_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0) {
+ *bridge = device;
+ *port = slave;
+ } else if (g_strcmp0 (device_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) {
+ *bridge = nm_device_get_master (device);
+ *port = device;
+ } else {
+ g_return_val_if_reached (FALSE);
+ }
+
+ if (!*bridge)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure)
+{
+ NMDevice *bridge = NULL;
+ NMDevice *port = NULL;
+
+ if (!configure)
+ return TRUE;
+
+ if (!_get_bridge_port (device, slave, connection, &bridge, &port))
+ return FALSE;
+
+ if (!bridge && !port)
+ return TRUE;
+
+ nm_ovsdb_transact (nm_ovsdb_get (), NM_OVSDB_ADD_IFACE,
+ nm_device_get_iface (bridge),
+ nm_device_get_applied_connection (port),
+ nm_device_get_applied_connection (slave),
+ add_iface_cb, g_object_ref (slave));
+
+ return TRUE;
+}
+
+static void
+del_iface_cb (GError *error, gpointer user_data)
+{
+ NMDevice *slave = user_data;
+
+ if (error) {
+ nm_log_warn (LOGD_DEVICE, "device %s could not be removed from a ovs port: %s",
+ nm_device_get_iface (slave), error->message);
+ nm_device_state_changed (slave,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_UNKNOWN);
+ }
+
+ g_object_unref (slave);
+}
+
+static void
+release_slave (NMDevice *device, NMDevice *slave, gboolean configure)
+{
+ NMDevice *bridge = NULL;
+ NMDevice *port = NULL;
+
+ if (!configure)
+ return;
+
+ if (!_get_bridge_port (device, slave, NULL, &bridge, &port))
+ return;
+
+ if (!bridge && !port)
+ return;
+
+ nm_ovsdb_transact (nm_ovsdb_get (), NM_OVSDB_DEL_IFACE,
+ nm_device_get_iface (bridge),
+ nm_device_get_applied_connection (port),
+ nm_device_get_applied_connection (slave),
+ del_iface_cb, g_object_ref (slave));
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_openvswitch_init (NMDeviceOpenvswitch *self)
+{
+}
+
+static void
+nm_device_openvswitch_class_init (NMDeviceOpenvswitchClass *klass)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+ NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH)
+
+ device_class->is_master = TRUE;
+ device_class->link_changed = link_changed;
+ device_class->create_and_realize = create_and_realize;
+ device_class->unrealize = unrealize;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->check_connection_compatible = check_connection_compatible;
+ device_class->check_slave_connection_compatible = check_slave_connection_compatible;
+ device_class->act_stage2_config = act_stage2_config;
+ device_class->enslave_slave = enslave_slave;
+ device_class->release_slave = release_slave;
+
+ nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+ NMDBUS_TYPE_DEVICE_OPENVSWITCH_SKELETON,
+ NULL);
+}