summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2017-08-24 18:43:55 +0200
committerLubomir Rintel <lkundrak@v3.sk>2017-09-11 22:35:06 +0200
commit60cd128ab77116d0faa7580d35e32609b60a06e9 (patch)
tree9b59e6d4ef4e8ffd654e84a0fda73f7eabe340f6
parente990f2b28ab767aea1ba2aee1f91eea8fe29ddd4 (diff)
fixup! devices: add support for openvswitch deviceslr/ovs-2
-rw-r--r--Makefile.am8
-rw-r--r--clients/common/nm-meta-setting-desc.c29
-rw-r--r--libnm-core/nm-connection.c31
-rw-r--r--libnm-core/nm-connection.h4
-rw-r--r--libnm-core/nm-core-internal.h3
-rw-r--r--libnm-core/nm-core-types.h3
-rw-r--r--libnm-core/nm-setting-ovs-bridge.c (renamed from libnm-core/nm-setting-openvswitch.c)32
-rw-r--r--libnm-core/nm-setting-ovs-bridge.h (renamed from libnm-core/nm-setting-openvswitch.h)32
-rw-r--r--libnm-core/nm-setting-ovs-port.c76
-rw-r--r--libnm-core/nm-setting-ovs-port.h63
-rw-r--r--libnm-core/nm-setting.c6
-rw-r--r--libnm/NetworkManager.h3
-rw-r--r--libnm/libnm.ver3
-rw-r--r--libnm/nm-device-openvswitch.c20
-rw-r--r--libnm/nm-device-openvswitch.h18
-rw-r--r--shared/nm-meta-setting.c16
-rw-r--r--shared/nm-meta-setting.h3
-rw-r--r--src/devices/openvswitch/nm-device-openvswitch.c196
-rw-r--r--src/devices/openvswitch/nm-openvswitch-factory.c934
-rw-r--r--src/devices/openvswitch/nm-openvswitch-factory.h53
-rw-r--r--src/devices/openvswitch/nm-ovsdb.c1203
-rw-r--r--src/devices/openvswitch/nm-ovsdb.h53
22 files changed, 1686 insertions, 1103 deletions
diff --git a/Makefile.am b/Makefile.am
index 27ef03577..c9d2818ac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -397,7 +397,8 @@ libnm_core_lib_h_pub_real = \
libnm-core/nm-setting-macsec.h \
libnm-core/nm-setting-macvlan.h \
libnm-core/nm-setting-olpc-mesh.h \
- libnm-core/nm-setting-openvswitch.h \
+ libnm-core/nm-setting-ovs-bridge.h \
+ libnm-core/nm-setting-ovs-port.h \
libnm-core/nm-setting-ppp.h \
libnm-core/nm-setting-pppoe.h \
libnm-core/nm-setting-proxy.h \
@@ -473,7 +474,8 @@ libnm_core_lib_c_real = \
libnm-core/nm-setting-macsec.c \
libnm-core/nm-setting-macvlan.c \
libnm-core/nm-setting-olpc-mesh.c \
- libnm-core/nm-setting-openvswitch.c \
+ libnm-core/nm-setting-ovs-bridge.c \
+ libnm-core/nm-setting-ovs-port.c \
libnm-core/nm-setting-ppp.c \
libnm-core/nm-setting-pppoe.c \
libnm-core/nm-setting-proxy.c \
@@ -2765,6 +2767,8 @@ endif
core_plugins += src/devices/openvswitch/libnm-device-plugin-openvswitch.la
src_devices_openvswitch_libnm_device_plugin_openvswitch_la_SOURCES = \
+ src/devices/openvswitch/nm-ovsdb.c \
+ src/devices/openvswitch/nm-ovsdb.h \
src/devices/openvswitch/nm-openvswitch-factory.c \
src/devices/openvswitch/nm-device-openvswitch.c \
src/devices/openvswitch/nm-device-openvswitch.h
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index 70b7ea062..c4f5638a0 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -5078,8 +5078,9 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
.values_static = VALUES_STATIC (NM_SETTING_BOND_SETTING_NAME,
NM_SETTING_BRIDGE_SETTING_NAME,
- NM_SETTING_TEAM_SETTING_NAME,
- NM_SETTING_OPENVSWITCH_SETTING_NAME),
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+ NM_SETTING_OVS_PORT_SETTING_NAME,
+ NM_SETTING_TEAM_SETTING_NAME),
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
@@ -6741,7 +6742,8 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
#define SETTING_PRETTY_NAME_MACSEC N_("MACsec connection")
#define SETTING_PRETTY_NAME_MACVLAN N_("macvlan connection")
#define SETTING_PRETTY_NAME_OLPC_MESH N_("OLPC Mesh connection")
-#define SETTING_PRETTY_NAME_OPENVSWITCH N_("OpenVSwitch settings")
+#define SETTING_PRETTY_NAME_OVS_BRIDGE N_("OpenVSwitch bridge settings")
+#define SETTING_PRETTY_NAME_OVS_PORT N_("OpenVSwitch port settings")
#define SETTING_PRETTY_NAME_PPP N_("PPP settings")
#define SETTING_PRETTY_NAME_PPPOE N_("PPPoE")
#define SETTING_PRETTY_NAME_PROXY N_("Proxy")
@@ -6891,10 +6893,18 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
),
.setting_init_fcn = _setting_init_fcn_olpc_mesh,
),
- SETTING_INFO_EMPTY (OPENVSWITCH,
+ SETTING_INFO_EMPTY (OVS_BRIDGE,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
- NM_META_SETTING_VALID_PART_ITEM (OPENVSWITCH, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (OVS_BRIDGE, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (OVS_PORT, FALSE),
+ NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE),
+ ),
+ ),
+ SETTING_INFO_EMPTY (OVS_PORT,
+ .valid_parts = NM_META_SETTING_VALID_PARTS (
+ NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (OVS_PORT, TRUE),
NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE),
),
),
@@ -7024,14 +7034,15 @@ nm_meta_setting_info_valid_parts_for_slave_type (const char *slave_type, const c
NM_SET_OUT (out_slave_name, "bridge-slave");
return valid_settings_slave_bridge;
}
+ if ( nm_streq (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)
+ || nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) {
+ NM_SET_OUT (out_slave_name, "ovs-slave");
+ return NM_PTRARRAY_EMPTY (const NMMetaSettingValidPartItem *);
+ }
if (nm_streq (slave_type, NM_SETTING_TEAM_SETTING_NAME)) {
NM_SET_OUT (out_slave_name, "team-slave");
return valid_settings_slave_team;
}
- if (nm_streq (slave_type, NM_SETTING_OPENVSWITCH_SETTING_NAME)) {
- NM_SET_OUT (out_slave_name, "ovs-slave");
- return NM_PTRARRAY_EMPTY (const NMMetaSettingValidPartItem *);
- }
return NULL;
}
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 692155ae0..4a00ba7b6 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -1974,7 +1974,8 @@ nm_connection_is_virtual (NMConnection *connection)
|| !strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME)
|| !strcmp (type, NM_SETTING_MACSEC_SETTING_NAME)
|| !strcmp (type, NM_SETTING_MACVLAN_SETTING_NAME)
- || !strcmp (type, NM_SETTING_OPENVSWITCH_SETTING_NAME)
+ || !strcmp (type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)
+ || !strcmp (type, NM_SETTING_OVS_PORT_SETTING_NAME)
|| !strcmp (type, NM_SETTING_VXLAN_SETTING_NAME))
return TRUE;
@@ -2328,19 +2329,35 @@ nm_connection_get_setting_olpc_mesh (NMConnection *connection)
}
/**
- * nm_connection_get_setting_openvswitch:
+ * nm_connection_get_setting_ovs_bridge:
* @connection: the #NMConnection
*
- * A shortcut to return any #NMSettingOpenvswitch the connection might contain.
+ * A shortcut to return any #NMSettingOvsBridge the connection might contain.
*
- * Returns: (transfer none): an #NMSettingOpenvswitch if the connection contains one, otherwise %NULL
+ * Returns: (transfer none): an #NMSettingOvsBridge if the connection contains one, otherwise %NULL
*
* Since: 1.10
**/
-NMSettingOpenvswitch *
-nm_connection_get_setting_openvswitch (NMConnection *connection)
+NMSettingOvsBridge *
+nm_connection_get_setting_ovs_bridge (NMConnection *connection)
{
- return _connection_get_setting_check (connection, NM_TYPE_SETTING_OPENVSWITCH);
+ return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_BRIDGE);
+}
+
+/**
+ * nm_connection_get_setting_ovs_port:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsPort the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsPort if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsPort *
+nm_connection_get_setting_ovs_port (NMConnection *connection)
+{
+ return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PORT);
}
/**
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index ae06b4006..ee6035a98 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -214,7 +214,9 @@ NM_AVAILABLE_IN_1_2
NMSettingMacvlan * nm_connection_get_setting_macvlan (NMConnection *connection);
NMSettingOlpcMesh * nm_connection_get_setting_olpc_mesh (NMConnection *connection);
NM_AVAILABLE_IN_1_10
-NMSettingOpenvswitch * nm_connection_get_setting_openvswitch (NMConnection *connection);
+NMSettingOvsBridge * nm_connection_get_setting_ovs_bridge (NMConnection *connection);
+NM_AVAILABLE_IN_1_10
+NMSettingOvsPort * nm_connection_get_setting_ovs_port (NMConnection *connection);
NMSettingPpp * nm_connection_get_setting_ppp (NMConnection *connection);
NMSettingPppoe * nm_connection_get_setting_pppoe (NMConnection *connection);
NM_AVAILABLE_IN_1_6
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 5ea96c08d..ab9967150 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -56,7 +56,8 @@
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
-#include "nm-setting-openvswitch.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-port.h"
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-serial.h"
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index 4eefefc1f..8c9bb852b 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -50,7 +50,8 @@ typedef struct _NMSettingIP6Config NMSettingIP6Config;
typedef struct _NMSettingMacsec NMSettingMacsec;
typedef struct _NMSettingMacvlan NMSettingMacvlan;
typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh;
-typedef struct _NMSettingOpenvswitch NMSettingOpenvswitch;
+typedef struct _NMSettingOvsBridge NMSettingOvsBridge;
+typedef struct _NMSettingOvsPort NMSettingOvsPort;
typedef struct _NMSettingPpp NMSettingPpp;
typedef struct _NMSettingPppoe NMSettingPppoe;
typedef struct _NMSettingSerial NMSettingSerial;
diff --git a/libnm-core/nm-setting-openvswitch.c b/libnm-core/nm-setting-ovs-bridge.c
index 493ef89f2..ec80870ea 100644
--- a/libnm-core/nm-setting-openvswitch.c
+++ b/libnm-core/nm-setting-ovs-bridge.c
@@ -20,37 +20,37 @@
#include "nm-default.h"
-#include "nm-setting-openvswitch.h"
+#include "nm-setting-ovs-bridge.h"
#include "nm-connection-private.h"
#include "nm-setting-connection.h"
#include "nm-setting-private.h"
/**
- * SECTION:nm-setting-openvswitch
- * @short_description: Describes connection properties for openvswitch interfaces
+ * SECTION:nm-setting-ovs-bridge
+ * @short_description: Describes connection properties for OpenVSwitch bridges
*
- * The #NMSettingOpenvswitch object is a #NMSetting subclass that describes properties
- * necessary for connection to openvswitch devices
+ * The #NMSettingOvsBridge object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch bridges.
**/
-G_DEFINE_TYPE_WITH_CODE (NMSettingOpenvswitch, nm_setting_openvswitch, NM_TYPE_SETTING,
- _nm_register_setting (OPENVSWITCH, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OPENVSWITCH)
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsBridge, nm_setting_ovs_bridge, NM_TYPE_SETTING,
+ _nm_register_setting (OVS_BRIDGE, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_BRIDGE)
/**
- * nm_setting_openvswitch_new:
+ * nm_setting_ovs_bridge_new:
*
- * Creates a new #NMSettingOpenvswitch object with default values.
+ * Creates a new #NMSettingOvsBridge object with default values.
*
- * Returns: (transfer full): the new empty #NMSettingOpenvswitch object
+ * Returns: (transfer full): the new empty #NMSettingOvsBridge object
*
* Since: 1.10
**/
NMSetting *
-nm_setting_openvswitch_new (void)
+nm_setting_ovs_bridge_new (void)
{
- return (NMSetting *) g_object_new (NM_TYPE_SETTING_OPENVSWITCH, NULL);
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_BRIDGE, NULL);
}
static gboolean
@@ -63,14 +63,14 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
}
static void
-nm_setting_openvswitch_init (NMSettingOpenvswitch *setting)
+nm_setting_ovs_bridge_init (NMSettingOvsBridge *setting)
{
}
static void
-nm_setting_openvswitch_class_init (NMSettingOpenvswitchClass *setting_class)
+nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *setting_class)
{
NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
- parent_class->verify = verify;
+ parent_class->verify = verify;
}
diff --git a/libnm-core/nm-setting-openvswitch.h b/libnm-core/nm-setting-ovs-bridge.h
index add1826c2..68829819e 100644
--- a/libnm-core/nm-setting-openvswitch.h
+++ b/libnm-core/nm-setting-ovs-bridge.h
@@ -17,8 +17,8 @@
* Copyright 2017 Red Hat, Inc.
*/
-#ifndef __NM_SETTING_OPENVSWITCH_H__
-#define __NM_SETTING_OPENVSWITCH_H__
+#ifndef __NM_SETTING_OVS_BRIDGE_H__
+#define __NM_SETTING_OVS_BRIDGE_H__
#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
#error "Only <NetworkManager.h> can be included directly."
@@ -28,21 +28,21 @@
G_BEGIN_DECLS
-#define NM_TYPE_SETTING_OPENVSWITCH (nm_setting_openvswitch_get_type ())
-#define NM_SETTING_OPENVSWITCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OPENVSWITCH, NMSettingOpenvswitch))
-#define NM_SETTING_OPENVSWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OPENVSWITCHCONFIG, NMSettingOpenvswitchClass))
-#define NM_IS_SETTING_OPENVSWITCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OPENVSWITCH))
-#define NM_IS_SETTING_OPENVSWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OPENVSWITCH))
-#define NM_SETTING_OPENVSWITCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OPENVSWITCH, NMSettingOpenvswitchClass))
+#define NM_TYPE_SETTING_OVS_BRIDGE (nm_setting_ovs_bridge_get_type ())
+#define NM_SETTING_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_BRIDGE, NMSettingOvsBridge))
+#define NM_SETTING_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_BRIDGECONFIG, NMSettingOvsBridgeClass))
+#define NM_IS_SETTING_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_BRIDGE))
+#define NM_IS_SETTING_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_BRIDGE))
+#define NM_SETTING_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_BRIDGE, NMSettingOvsBridgeClass))
-#define NM_SETTING_OPENVSWITCH_SETTING_NAME "openvswitch"
+#define NM_SETTING_OVS_BRIDGE_SETTING_NAME "ovs-bridge"
/**
- * NMSettingOpenvswitch:
+ * NMSettingOvsBridge:
*
- * Openvswitch Link Settings
+ * OvsBridge Link Settings
*/
-struct _NMSettingOpenvswitch {
+struct _NMSettingOvsBridge {
NMSetting parent;
};
@@ -51,13 +51,13 @@ typedef struct {
/*< private >*/
gpointer padding[4];
-} NMSettingOpenvswitchClass;
+} NMSettingOvsBridgeClass;
NM_AVAILABLE_IN_1_10
-GType nm_setting_openvswitch_get_type (void);
+GType nm_setting_ovs_bridge_get_type (void);
NM_AVAILABLE_IN_1_10
-NMSetting *nm_setting_openvswitch_new (void);
+NMSetting *nm_setting_ovs_bridge_new (void);
G_END_DECLS
-#endif /* __NM_SETTING_OPENVSWITCH_H__ */
+#endif /* __NM_SETTING_OVS_BRIDGE_H__ */
diff --git a/libnm-core/nm-setting-ovs-port.c b/libnm-core/nm-setting-ovs-port.c
new file mode 100644
index 000000000..a141adfa1
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-port.c
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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-setting-ovs-port.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-port
+ * @short_description: Describes connection properties for OpenVSwitch ports.
+ *
+ * The #NMSettingOvsPort object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch ports.
+ **/
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsPort, nm_setting_ovs_port, NM_TYPE_SETTING,
+ _nm_register_setting (OVS_PORT, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_PORT)
+
+/**
+ * nm_setting_ovs_port_new:
+ *
+ * Creates a new #NMSettingOvsPort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsPort object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_port_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_PORT, NULL);
+}
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ if (!_nm_connection_verify_required_interface_name (connection, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+nm_setting_ovs_port_init (NMSettingOvsPort *setting)
+{
+}
+
+static void
+nm_setting_ovs_port_class_init (NMSettingOvsPortClass *setting_class)
+{
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ parent_class->verify = verify;
+}
diff --git a/libnm-core/nm-setting-ovs-port.h b/libnm-core/nm-setting-ovs-port.h
new file mode 100644
index 000000000..66ec21f12
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-port.h
@@ -0,0 +1,63 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_OVS_PORT_H__
+#define __NM_SETTING_OVS_PORT_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OVS_PORT (nm_setting_ovs_port_get_type ())
+#define NM_SETTING_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_PORT, NMSettingOvsPort))
+#define NM_SETTING_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_PORTCONFIG, NMSettingOvsPortClass))
+#define NM_IS_SETTING_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_PORT))
+#define NM_IS_SETTING_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_PORT))
+#define NM_SETTING_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_PORT, NMSettingOvsPortClass))
+
+#define NM_SETTING_OVS_PORT_SETTING_NAME "ovs-port"
+
+/**
+ * NMSettingOvsPort:
+ *
+ * OvsPort Link Settings
+ */
+struct _NMSettingOvsPort {
+ NMSetting parent;
+};
+
+typedef struct {
+ NMSettingClass parent;
+
+ /*< private >*/
+ gpointer padding[4];
+} NMSettingOvsPortClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_port_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_port_new (void);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_PORT_H__ */
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index eed23c3e7..4992eac69 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -258,10 +258,12 @@ _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_t
;
else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME))
port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME;
+ else if (!strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
+ ;
+ else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
+ ;
else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
port_type = NM_SETTING_TEAM_PORT_SETTING_NAME;
- else if (!strcmp (slave_type, NM_SETTING_OPENVSWITCH_SETTING_NAME))
- ;
else
found = FALSE;
diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h
index 32aac7062..8bbb65891 100644
--- a/libnm/NetworkManager.h
+++ b/libnm/NetworkManager.h
@@ -74,7 +74,8 @@
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
-#include "nm-setting-openvswitch.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-port.h"
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-proxy.h"
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 7c4796827..d52450eac 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1187,7 +1187,8 @@ global:
nm_device_openvswitch_get_type;
nm_device_ppp_get_type;
nm_setting_bridge_get_group_forward_mask;
- nm_setting_openvswitch_get_type;
+ nm_setting_ovs_bridge_get_type;
+ nm_setting_ovs_port_get_type;
nm_setting_pppoe_get_parent;
nm_setting_wireless_security_get_pmf;
nm_setting_wireless_security_get_wps_method;
diff --git a/libnm/nm-device-openvswitch.c b/libnm/nm-device-openvswitch.c
index 5860ec764..2b33b220c 100644
--- a/libnm/nm-device-openvswitch.c
+++ b/libnm/nm-device-openvswitch.c
@@ -23,9 +23,21 @@
#include "nm-device-openvswitch.h"
#include "nm-object-private.h"
-#include "nm-setting-openvswitch.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-port.h"
#include "nm-setting-connection.h"
+/**
+ * NMDeviceOpenvswitch:
+ */
+struct _NMDeviceOpenvswitch {
+ NMDevice parent;
+};
+
+typedef struct {
+ NMDeviceClass parent;
+} NMDeviceOpenvswitchClass;
+
G_DEFINE_TYPE (NMDeviceOpenvswitch, nm_device_openvswitch, NM_TYPE_DEVICE)
/*****************************************************************************/
@@ -38,7 +50,8 @@ connection_compatible (NMDevice *device, NMConnection *connection, GError **erro
if (!NM_DEVICE_CLASS (nm_device_openvswitch_parent_class)->connection_compatible (device, connection, error))
return FALSE;
- if (!nm_connection_is_type (connection, NM_SETTING_OPENVSWITCH_SETTING_NAME)) {
+ if ( !nm_connection_is_type (connection, NM_SETTING_OVS_BRIDGE_SETTING_NAME)
+ && !nm_connection_is_type (connection, NM_SETTING_OVS_PORT_SETTING_NAME)) {
g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
_("The connection was not a openvswitch connection."));
return FALSE;
@@ -57,7 +70,8 @@ connection_compatible (NMDevice *device, NMConnection *connection, GError **erro
static GType
get_setting_type (NMDevice *device)
{
- return NM_TYPE_SETTING_OPENVSWITCH;
+ // XXX
+ return NM_TYPE_SETTING_OVS_PORT;
}
/*****************************************************************************/
diff --git a/libnm/nm-device-openvswitch.h b/libnm/nm-device-openvswitch.h
index 239bb4d6f..126f488b6 100644
--- a/libnm/nm-device-openvswitch.h
+++ b/libnm/nm-device-openvswitch.h
@@ -35,25 +35,7 @@ G_BEGIN_DECLS
#define NM_IS_DEVICE_OPENVSWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OPENVSWITCH))
#define NM_DEVICE_OPENVSWITCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OPENVSWITCH, NMDeviceOpenvswitchClass))
-#define NM_DEVICE_OPENVSWITCH_HW_ADDRESS "hw-address"
-
-/**
- * NMDeviceOpenvswitch:
- */
-struct _NMDeviceOpenvswitch {
- NMDevice parent;
-};
-
-typedef struct {
- NMDeviceClass parent;
-
- /*< private >*/
- gpointer padding[4];
-} NMDeviceOpenvswitchClass;
-
GType nm_device_openvswitch_get_type (void);
-NM_AVAILABLE_IN_1_10
-const char *nm_device_openvswitch_get_hw_address (NMDeviceOpenvswitch *device);
G_END_DECLS
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index 24f623da9..fde965e50 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -43,7 +43,8 @@
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
-#include "nm-setting-openvswitch.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-port.h"
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-proxy.h"
@@ -236,10 +237,15 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_OLPC_MESH_SETTING_NAME,
.get_setting_gtype = nm_setting_olpc_mesh_get_type,
},
- [NM_META_SETTING_TYPE_OPENVSWITCH] = {
- .meta_type = NM_META_SETTING_TYPE_OPENVSWITCH,
- .setting_name = NM_SETTING_OPENVSWITCH_SETTING_NAME,
- .get_setting_gtype = nm_setting_openvswitch_get_type,
+ [NM_META_SETTING_TYPE_OVS_BRIDGE] = {
+ .meta_type = NM_META_SETTING_TYPE_OVS_BRIDGE,
+ .setting_name = NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+ .get_setting_gtype = nm_setting_ovs_bridge_get_type,
+ },
+ [NM_META_SETTING_TYPE_OVS_PORT] = {
+ .meta_type = NM_META_SETTING_TYPE_OVS_PORT,
+ .setting_name = NM_SETTING_OVS_PORT_SETTING_NAME,
+ .get_setting_gtype = nm_setting_ovs_port_get_type,
},
[NM_META_SETTING_TYPE_PPPOE] = {
.meta_type = NM_META_SETTING_TYPE_PPPOE,
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index d928c0b8c..53c50127d 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -75,7 +75,8 @@ typedef enum {
NM_META_SETTING_TYPE_MACSEC,
NM_META_SETTING_TYPE_MACVLAN,
NM_META_SETTING_TYPE_OLPC_MESH,
- NM_META_SETTING_TYPE_OPENVSWITCH,
+ NM_META_SETTING_TYPE_OVS_BRIDGE,
+ NM_META_SETTING_TYPE_OVS_PORT,
NM_META_SETTING_TYPE_PPP,
NM_META_SETTING_TYPE_PPPOE,
NM_META_SETTING_TYPE_PROXY,
diff --git a/src/devices/openvswitch/nm-device-openvswitch.c b/src/devices/openvswitch/nm-device-openvswitch.c
index 0f2e14d62..32fdf0dc0 100644
--- a/src/devices/openvswitch/nm-device-openvswitch.c
+++ b/src/devices/openvswitch/nm-device-openvswitch.c
@@ -20,10 +20,12 @@
#include "nm-default.h"
#include "nm-device-openvswitch.h"
-#include "nm-openvswitch-factory.h"
+#include "nm-ovsdb.h"
#include "devices/nm-device-private.h"
-#include "nm-setting-openvswitch.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"
@@ -79,18 +81,25 @@ create_and_realize (NMDevice *device,
const NMPlatformLink **out_plink,
GError **error)
{
- NMDeviceFactory *factory;
-
- factory = nm_device_factory_manager_find_factory_for_link_type (NM_LINK_TYPE_OPENVSWITCH);
- g_return_val_if_fail (factory, FALSE);
-
- nm_openvswitch_factory_add_br (NM_OPENVSWITCH_FACTORY (factory),
- nm_device_get_iface (device),
- 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. */
+ 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_add_br (nm_ovsdb_get (),
+ nm_device_get_iface (device),
+ 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;
}
@@ -113,15 +122,10 @@ del_br_cb (GError *error, gpointer user_data)
static gboolean
unrealize (NMDevice *device, GError **error)
{
- NMDeviceFactory *factory;
-
- factory = nm_device_factory_manager_find_factory_for_link_type (NM_LINK_TYPE_OPENVSWITCH);
- g_return_val_if_fail (factory, FALSE);
-
- nm_openvswitch_factory_del_br (NM_OPENVSWITCH_FACTORY (factory),
- nm_device_get_iface (device),
- del_br_cb,
- g_object_ref (device));
+ nm_ovsdb_del_br (nm_ovsdb_get (),
+ nm_device_get_iface (device),
+ del_br_cb,
+ g_object_ref (device));
return TRUE;
}
@@ -136,22 +140,60 @@ get_generic_capabilities (NMDevice *device)
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection)
{
- NMSettingOpenvswitch *s_openvswitch;
+ NMSettingConnection *s_con;
+ const char *connection_type;
if (!NM_DEVICE_CLASS (nm_device_openvswitch_parent_class)->check_connection_compatible (device, connection))
return FALSE;
- s_openvswitch = nm_connection_get_setting_openvswitch (connection);
- if (!s_openvswitch)
+ 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;
- return TRUE;
+ // XXX
+ 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;
+
+ // XXX
+ 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;
@@ -164,12 +206,15 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
static void
update_connection (NMDevice *device, NMConnection *connection)
{
+ // XXX
+#if 0
NMSettingOpenvswitch *s_openvswitch = nm_connection_get_setting_openvswitch (connection);
if (!s_openvswitch) {
s_openvswitch = (NMSettingOpenvswitch *) nm_setting_openvswitch_new ();
nm_connection_add_setting (connection, (NMSetting *) s_openvswitch);
}
+#endif
}
static void
@@ -189,21 +234,68 @@ add_port_cb (GError *error, gpointer user_data)
}
static gboolean
-enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure)
+_get_bridge_port (NMDevice *device, NMDevice *slave, NMConnection *connection,
+ NMDevice **bridge, NMDevice **port)
{
- NMDeviceFactory *factory;
+ NMConnection *applied_connection;
+ const char *device_type;
+ const char *slave_type = NULL;
- factory = nm_device_factory_manager_find_factory_for_link_type (NM_LINK_TYPE_OPENVSWITCH);
- g_return_val_if_fail (factory, FALSE);
+ if (!connection)
+ connection = nm_device_get_applied_connection (slave);
+ if (connection)
+ slave_type = nm_connection_get_connection_type (connection);
- if (configure) {
- nm_openvswitch_factory_add_port (NM_OPENVSWITCH_FACTORY (factory),
- nm_device_get_iface (device),
- nm_device_get_iface (slave),
- add_port_cb,
- g_object_ref (slave));
+ 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_add_port (nm_ovsdb_get (),
+ nm_device_get_iface (bridge),
+ nm_device_get_iface (port),
+ nm_device_get_iface (slave),
+ add_port_cb,
+ g_object_ref (slave));
+
return TRUE;
}
@@ -226,18 +318,24 @@ del_port_cb (GError *error, gpointer user_data)
static void
release_slave (NMDevice *device, NMDevice *slave, gboolean configure)
{
- NMDeviceFactory *factory;
+ NMDevice *bridge = NULL;
+ NMDevice *port = NULL;
- factory = nm_device_factory_manager_find_factory_for_link_type (NM_LINK_TYPE_OPENVSWITCH);
- g_return_if_fail (factory);
+ if (!configure)
+ return;
- if (configure) {
- nm_openvswitch_factory_del_port (NM_OPENVSWITCH_FACTORY (factory),
- nm_device_get_iface (device),
- nm_device_get_iface (slave),
- del_port_cb,
- g_object_ref (slave));
- }
+ if (!_get_bridge_port (device, slave, NULL, &bridge, &port))
+ return;
+
+ if (!bridge && !port)
+ return;
+
+ nm_ovsdb_del_port (nm_ovsdb_get (),
+ nm_device_get_iface (bridge),
+ nm_device_get_iface (port),
+ nm_device_get_iface (slave),
+ del_port_cb,
+ g_object_ref (slave));
}
/*****************************************************************************/
@@ -254,7 +352,8 @@ nm_device_openvswitch_class_init (NMDeviceOpenvswitchClass *klass)
NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH)
- device_class->connection_type = NM_SETTING_OPENVSWITCH_SETTING_NAME;
+ // XXX
+ //device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME;
device_class->is_master = TRUE;
device_class->link_changed = link_changed;
@@ -262,6 +361,7 @@ nm_device_openvswitch_class_init (NMDeviceOpenvswitchClass *klass)
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->update_connection = update_connection;
device_class->enslave_slave = enslave_slave;
diff --git a/src/devices/openvswitch/nm-openvswitch-factory.c b/src/devices/openvswitch/nm-openvswitch-factory.c
index b99d469e3..caae06712 100644
--- a/src/devices/openvswitch/nm-openvswitch-factory.c
+++ b/src/devices/openvswitch/nm-openvswitch-factory.c
@@ -19,57 +19,31 @@
#include "nm-default.h"
-#include "nm-openvswitch-factory.h"
-
-#include <string.h>
-#include <jansson.h>
-#include <gmodule.h>
-#include <gio/gunixsocketaddress.h>
-
+#include "nm-ovsdb.h"
#include "nm-device-openvswitch.h"
#include "platform/nm-platform.h"
#include "nm-core-internal.h"
+#include "devices/nm-device-factory.h"
/*****************************************************************************/
typedef struct {
- char *name;
- GPtrArray *interfaces; /* interface uuids */
-} OpenvswitchPort;
-
-typedef struct {
- char *name;
- GPtrArray *ports; /* port uuids */
-} OpenvswitchBridge;
-
-typedef struct {
- GSocketClient *client;
- GSocketConnection *conn;
- GCancellable *cancellable;
- char buf[4096]; /* Input buffer */
- size_t bufp; /* Last decoded byte in the input buffer. */
- GString *input; /* JSON stream waiting for decoding. */
- GString *output; /* JSON stream to be sent. */
- guint64 seq;
- GArray *calls; /* Method calls waiting for a response. */
- GHashTable *interfaces; /* interface uuid => interface name */
- GHashTable *ports; /* port uuid => OpenvswitchBridge */
- GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */
- const char *db_uuid;
-} NMOpenvswitchFactoryPrivate;
-
-struct _NMOpenvswitchFactory {
NMDeviceFactory parent;
- NMOpenvswitchFactoryPrivate _priv;
-};
+} NMOpenvswitchFactory;
-struct _NMOpenvswitchFactoryClass {
+typedef struct {
NMDeviceFactoryClass parent;
-};
+} NMOpenvswitchFactoryClass;
-G_DEFINE_TYPE (NMOpenvswitchFactory, nm_openvswitch_factory, NM_TYPE_DEVICE_FACTORY)
+#define NM_TYPE_OPENVSWITCH_FACTORY (nm_openvswitch_factory_get_type ())
+#define NM_OPENVSWITCH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OPENVSWITCH_FACTORY, NMOpenvswitchFactory))
+#define NM_OPENVSWITCH_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OPENVSWITCH_FACTORY, NMOpenvswitchFactoryClass))
+#define NM_IS_OPENVSWITCH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OPENVSWITCH_FACTORY))
+#define NM_IS_OPENVSWITCH_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OPENVSWITCH_FACTORY))
+#define NM_OPENVSWITCH_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OPENVSWITCH_FACTORY, NMOpenvswitchFactoryClass))
-#define NM_OPENVSWITCH_FACTORY_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMOpenvswitchFactory, NM_IS_OPENVSWITCH_FACTORY)
+static GType nm_openvswitch_factory_get_type (void);
+G_DEFINE_TYPE (NMOpenvswitchFactory, nm_openvswitch_factory, NM_TYPE_DEVICE_FACTORY)
/*****************************************************************************/
@@ -78,830 +52,10 @@ G_DEFINE_TYPE (NMOpenvswitchFactory, nm_openvswitch_factory, NM_TYPE_DEVICE_FACT
/*****************************************************************************/
-static void ovsdb_try_connect (NMOpenvswitchFactory *self);
-static void ovsdb_disconnect (NMOpenvswitchFactory *self);
-static void ovsdb_read (NMOpenvswitchFactory *self);
-static void ovsdb_write (NMOpenvswitchFactory *self);
-static void ovsdb_next_command (NMOpenvswitchFactory *self);
-
-/*****************************************************************************/
-
-/* ovsdb command abstraction. */
-
-typedef void (*OvsdbMethodCallback) (NMOpenvswitchFactory *self, json_t *response,
- GError *error, gpointer user_data);
-
-typedef enum {
- OVSDB_MONITOR,
- OVSDB_ADD_BR,
- OVSDB_DEL_BR,
- OVSDB_ADD_PORT,
- OVSDB_DEL_PORT,
-} OvsdbCommand;
-
-typedef struct {
- guint64 id;
- OvsdbMethodCallback callback;
- gpointer user_data;
- OvsdbCommand command;
- char bridge_iface[IFNAMSIZ + 1]; /* Used by add and del commands. */
- char port_iface[IFNAMSIZ + 1]; /* Used by port commands. */
-} OvsdbMethodCall;
-
-/**
- * ovsdb_call_method:
- *
- * Queues the ovsdb command. Eventually fires the command right away if
- * there's no command pending completion.
- */
-static void
-ovsdb_call_method (NMOpenvswitchFactory *self, OvsdbCommand command,
- const char *bridge_iface, const char *port_iface,
- OvsdbMethodCallback callback, gpointer user_data)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- OvsdbMethodCall *call;
-
- /* Ensure we're not unsynchronized before we queue the method call. */
- ovsdb_try_connect (self);
-
- g_array_set_size (priv->calls, priv->calls->len + 1);
- call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1);
- call->id = priv->seq++;
- call->command = command;
- if (bridge_iface) {
- /* The add and del commands use a bridge_iface parameter. */
- strcpy (call->bridge_iface, bridge_iface);
- }
- if (port_iface) {
- /* The port commands use a port_iface parameter. */
- strcpy (call->port_iface, port_iface);
- }
- call->callback = callback;
- call->user_data = user_data;
-
- if (priv->calls->len == 1) {
- /* There was no command waiting for completion -- we're free
- * to ahead and proceed serializing and write this one without
- * waiting for a command to complete. */
- ovsdb_next_command (self);
- }
-}
-
-/*****************************************************************************/
-
-/* Create and process the JSON-RPC messages from ovsdb. */
-
-/*
- * _fill_ports:
- *
- * Put set of all ports of @bridge_iface into @items and all but
- * @exclude_port_iface into @new_items.
- */
-static void
-_fill_ports (NMOpenvswitchFactory *self,
- const char *bridge_iface, const char *exclude_port_iface,
- json_t **items, json_t **new_items)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- GHashTableIter iter;
- char *bridge_uuid;
- char *port_uuid;
- OpenvswitchBridge *ovs_bridge;
- OpenvswitchPort *ovs_port;
- int i;
-
- *items = json_array ();
- *new_items = json_array ();
-
- g_hash_table_iter_init (&iter, priv->bridges);
- while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) {
- if (g_strcmp0 (ovs_bridge->name, bridge_iface) != 0)
- continue;
- for (i = 0; i < ovs_bridge->ports->len; i++) {
- port_uuid = g_ptr_array_index (ovs_bridge->ports, i);
- json_array_append_new (*items, json_pack ("[s,s]", "uuid", port_uuid));
-
- ovs_port = g_hash_table_lookup (priv->ports, port_uuid);
- if (!ovs_port)
- continue;
- if (g_strcmp0 (exclude_port_iface, ovs_port->name) == 0)
- continue;
- json_array_append_new (*new_items, json_pack ("[s,s]", "uuid", port_uuid));
- }
- }
-}
-
-/**
- * _fill_bridges:
- *
- * Put set of all bridges into @items and all but @exclude_bridge_iface into
- * @new_items.
- */
-static void
-_fill_bridges (NMOpenvswitchFactory *self, const char *exclude_bridge_iface,
- json_t **items, json_t **new_items)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- GHashTableIter iter;
- char *bridge_uuid;
- OpenvswitchBridge *ovs_bridge;
-
- *items = json_array ();
- *new_items = json_array ();
-
- g_hash_table_iter_init (&iter, priv->bridges);
- while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) {
- json_array_append_new (*items, json_pack ("[s,s]", "uuid", bridge_uuid));
- if (g_strcmp0 (exclude_bridge_iface, ovs_bridge->name) != 0)
- json_array_append_new (*new_items, json_pack ("[s,s]", "uuid", bridge_uuid));
- }
-}
-
-/**
- * _expect_ports:
- *
- * Return a command that will fail the transaction if the actual set of
- * ports in @bridge_iface doesn't match @ports. This is a way of detecting
- * race conditions with other ovsdb clients that might be adding or removing
- * bridge ports at the same time.
- */
-static json_t *
-_expect_ports (const char *bridge_iface, const json_t *ports)
-{
- return json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, o]}], s:[[s, s, s]]}",
- "op", "wait", "table", "Bridge",
- "timeout", 0, "columns", "ports",
- "until", "==", "rows", "ports", "set", ports,
- "where", "name", "==", bridge_iface);
-}
-
-/**
- * _set_ports:
- *
- * Return a command that will update the list of ports of @bridge_iface
- * to @ports.
- */
-static json_t *
-_set_ports (const char *bridge_iface, const json_t *ports)
-{
- return json_pack ("{s:s, s:s, s:{s:[s, o]}, s:[[s, s, s]]}",
- "op", "update", "table", "Bridge",
- "row", "ports", "set", ports,
- "where", "name", "==", bridge_iface);
-}
-
-/**
- * _expect_bridges:
- *
- * Return a command that will fail the transaction if the actual set of
- * bridges doesn't match @bridges. This is a way of detecting race conditions
- * with other ovsdb clients that might be adding or removing bridges
- * at the same time.
- */
-static json_t *
-_expect_bridges (json_t *bridges, const char *db_uuid)
-{
- return json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, o]}], s:[[s, s, [s, s]]]}",
- "op", "wait", "table", "Open_vSwitch",
- "timeout", 0, "columns", "bridges",
- "until", "==", "rows", "bridges", "set", bridges,
- "where", "_uuid", "==", "uuid", db_uuid);
-}
-
-/**
- * _set_bridges:
- *
- * Return a command that will update the list of bridges in @db_uuid
- * database to @bridges.
- */
-static json_t *
-_set_bridges (const json_t *bridges, const char *db_uuid)
-{
- return json_pack ("{s:s, s:s, s:{s:[s, o]}, s:[[s, s, [s, s]]]}",
- "op", "update", "table", "Open_vSwitch",
- "row", "bridges", "set", bridges,
- "where", "_uuid", "==", "uuid", db_uuid);
-}
-
-/**
- * _inc_next_cfg:
- *
- * Returns an mutate commands that bumps next_cfg upon successful completion
- * of the transaction it is in.
- */
-static json_t *
-_inc_next_cfg (const char *db_uuid)
-{
- return json_pack ("{s:s, s:s, s:[[s, s, i]], s:[[s, s, [s, s]]]}",
- "op", "mutate", "table", "Open_vSwitch",
- "mutations", "next_cfg", "+=", 1,
- "where", "_uuid", "==", "uuid", db_uuid);
-}
-
-/**
- * ovsdb_next_command:
- *
- * Translates a higher level operation (add/remove bridge/port) to a RFC 7047
- * command serialized into JSON ands sends it over to the database.
-
- * Only called when no command is waiting for a response, since the serialized
- * command might depend on result of a previous one (add and remove need to
- * include an up to date bridge list in their transactions to rule out races).
- */
-static void
-ovsdb_next_command (NMOpenvswitchFactory *self)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- OvsdbMethodCall *call = NULL;
- char *cmd;
- json_t *msg = NULL;
- json_t *items, *new_items;
-
- if (!priv->conn)
- return;
- if (!priv->calls->len)
- return;
- call = &g_array_index (priv->calls, OvsdbMethodCall, 0);
-
- switch (call->command) {
- case OVSDB_MONITOR:
- msg = json_pack ("{s:i, s:s, s:[s, n, {"
- " s:[{s:[s, s]}],"
- " s:[{s:[s, s]}],"
- " s:[{s:[s]}],"
- " s:[{s:[]}]"
- "}]}",
- "id", call->id,
- "method", "monitor", "params", "Open_vSwitch",
- "Bridge", "columns", "name", "ports",
- "Port", "columns", "name", "interfaces",
- "Interface", "columns", "name",
- "Open_vSwitch", "columns");
- break;
- case OVSDB_ADD_BR:
- _fill_bridges (self, call->bridge_iface, &items, &new_items);
- json_array_append_new (new_items, json_pack ("[s,s]", "named-uuid", "rowBridge"));
-
- msg = json_pack ("{s:i, s:s, s:[s,o,o,o"
- " {s:s, s:s, s:{s:s, s:s}, s:s}, " /* insert interface */
- " {s:s, s:s, s:{s:s, s:[s, s]}, s:s}," /* insert port */
- " {s:s, s:s, s:{s:s, s:[s, s]}, s:s}," /* insert bridge */
- "]}",
- "id", call->id,
- "method", "transact", "params", "Open_vSwitch",
- _expect_bridges (items, priv->db_uuid),
- _set_bridges (new_items, priv->db_uuid),
- _inc_next_cfg (priv->db_uuid),
- "op", "insert", "table", "Interface", "row", "name", call->bridge_iface,
- "type", "internal", "uuid-name", "rowIntf",
- "op", "insert", "table", "Port", "row", "name", call->bridge_iface,
- "interfaces", "named-uuid", "rowIntf", "uuid-name", "rowPort",
- "op", "insert", "table", "Bridge", "row", "name", call->bridge_iface,
- "ports", "named-uuid", "rowPort", "uuid-name", "rowBridge");
- break;
- case OVSDB_DEL_BR:
- _fill_bridges (self, call->bridge_iface, &items, &new_items);
-
- msg = json_pack ("{s:i, s:s, s:[s,o,o,o]}",
- "id", call->id,
- "method", "transact", "params", "Open_vSwitch",
- _expect_bridges (items, priv->db_uuid),
- _set_bridges (new_items, priv->db_uuid),
- _inc_next_cfg (priv->db_uuid));
- break;
- case OVSDB_ADD_PORT:
- _fill_ports (self, call->bridge_iface, call->port_iface, &items, &new_items);
- json_array_append_new (new_items, json_pack ("[s,s]", "named-uuid", "rowPort"));
-
- msg = json_pack ("{s:i, s:s, s:[s,o,o,o"
- " {s:s, s:s, s:{s:s}, s:s}, " /* insert interface */
- " {s:s, s:s, s:{s:s, s:[s, s]}, s:s}," /* insert port */
- "]}",
- "id", call->id,
- "method", "transact", "params", "Open_vSwitch",
- _expect_ports (call->bridge_iface, items),
- _set_ports (call->bridge_iface, new_items),
- _inc_next_cfg (priv->db_uuid),
- "op", "insert", "table", "Interface", "row", "name", call->port_iface,
- "uuid-name", "rowIntf",
- "op", "insert", "table", "Port", "row", "name", call->port_iface,
- "interfaces", "named-uuid", "rowIntf", "uuid-name", "rowPort");
- break;
- case OVSDB_DEL_PORT:
- _fill_ports (self, call->bridge_iface, call->port_iface, &items, &new_items);
-
- msg = json_pack ("{s:i, s:s, s:[s,o,o,o]}",
- "id", call->id,
- "method", "transact", "params", "Open_vSwitch",
- _expect_ports (call->bridge_iface, items),
- _set_ports (call->bridge_iface, new_items),
- _inc_next_cfg (priv->db_uuid));
- break;
- }
-
- g_return_if_fail (msg);
- cmd = json_dumps (msg, 0);
- g_string_append (priv->output, cmd);
- json_decref (msg);
- free (cmd);
-
- ovsdb_write (self);
-}
-
-static void
-_uuids_to_array (GPtrArray *array, const json_t *items)
-{
- const char *key;
- json_t *value;
- size_t index = 0;
- json_t *set_value;
- size_t set_index;
-
- while (index < json_array_size (items)) {
- key = json_string_value (json_array_get (items, index));
- index++;
- value = json_array_get (items, index);
- index++;
-
- if (!value)
- return;
-
- if (g_strcmp0 (key, "uuid") == 0 && json_is_string (value)) {
- g_ptr_array_add (array, g_strdup (json_string_value (value)));
- } else if (g_strcmp0 (key, "set") == 0 && json_is_array (value)) {
- json_array_foreach (value, set_index, set_value) {
- _uuids_to_array (array, set_value);
- }
- }
- }
-}
-
-/**
- * ovsdb_got_update:
- *
- * Called when we've got an "update" method call (we asked for it with the monitor
- * command). We use it to maintain a consistent view of bridge list regardless of
- * whether the changes are done by us or externally.
- */
-static void
-ovsdb_got_update (NMOpenvswitchFactory *self, json_t *msg)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- json_t *ovs = NULL;
- json_t *bridge = NULL;
- json_t *port = NULL;
- json_t *interface = NULL;
- json_t *items;
- json_error_t json_error = { 0, };
- void *iter;
- const char *name;
- const char *key;
- json_t *value;
- OpenvswitchBridge *ovs_bridge;
- OpenvswitchPort *ovs_port;
-
- if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:o, s?:o, s?:o}}",
- "Open_vSwitch", &ovs,
- "Bridge", &bridge,
- "Port", &port,
- "Interface", &interface) == -1) {
- /* This doesn't really have to be an error; the key might
- * be missing if there really are no bridges present. */
- _LOGD ("Bad update: %s", json_error.text);
- }
-
- if (ovs) {
- iter = json_object_iter (ovs);
- priv->db_uuid = g_strdup (iter ? json_object_iter_key (iter) : NULL);
- }
-
- json_object_foreach (port, key, value) {
- if (json_unpack (value, "{s:{}}", "old") == 0) {
- _LOGT ("removed a port: %s", name);
- g_hash_table_remove (priv->ports, key);
- }
- if (json_unpack (value, "{s:{s?:s, s?:o}}", "new", "name", &name, "interfaces", &items) == 0) {
- _LOGT ("added a port: %s", name);
- ovs_port = g_slice_new (OpenvswitchPort);
- ovs_port->name = g_strdup (name);
- ovs_port->interfaces = g_ptr_array_new_with_free_func (g_free);
- _uuids_to_array (ovs_port->interfaces, items);
- g_hash_table_insert (priv->ports, g_strdup (key), ovs_port);
- }
- }
-
- json_object_foreach (bridge, key, value) {
- if (json_unpack (value, "{s:{}}", "old") == 0) {
- _LOGT ("removed a bridge: %s", name);
- g_hash_table_remove (priv->bridges, key);
- }
- if (json_unpack (value, "{s:{s?:s, s?:o}}", "new", "name", &name, "ports", &items) == 0) {
- _LOGT ("added a bridge: %s", name);
- ovs_bridge = g_slice_new (OpenvswitchBridge);
- ovs_bridge->name = g_strdup (name);
- ovs_bridge->ports = g_ptr_array_new_with_free_func (g_free);
- _uuids_to_array (ovs_bridge->ports, items);
- g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge);
- }
- }
-}
-
-/**
- * ovsdb_got_msg::
- *
- * Called when when a complete JSON object was seen and unmarshalled.
- * Either finishes a method call or processes a method call.
- */
-static void
-ovsdb_got_msg (NMOpenvswitchFactory *self, json_t *msg)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- json_t *json_id = NULL;
- guint64 id;
- json_t *json_method = NULL;
- const char *method = NULL;
- OvsdbMethodCall *call = NULL;
- json_error_t json_error = { 0, };
- json_t *params = NULL;
-
- json_id = json_object_get (msg, "id");
- if (json_is_number (json_id)) {
- /* This is a response to a method call. */
- id = json_integer_value (json_id);
-
- if (!priv->calls->len) {
- _LOGE ("there are no queued calls expecting response %ld", id);
- ovsdb_disconnect (self);
- return;
- }
- call = &g_array_index (priv->calls, OvsdbMethodCall, 0);
- if (call->id != id) {
- _LOGE ("expected a response to call %ld, not %ld", call->id, id);
- ovsdb_disconnect (self);
- return;
- }
-
- /* Cool, we found a corresponsing call. Finish it. */
- call->callback (self, msg, NULL, call->user_data);
- g_array_remove_index (priv->calls, 0);
-
- /* Now we're free to serialize and send the next command, if any. */
- ovsdb_next_command (self);
- return;
- }
-
- json_method = json_object_get (msg, "method");
- if (json_is_string (json_method))
- method = json_string_value (json_method);
- if (g_strcmp0 (method, "update") == 0) {
- /* This is a update method call. */
- if (json_unpack_ex (msg, &json_error, 0, "{s:[n,o]}",
- "params", &params) == -1) {
- _LOGD ("a update call with no params: %s", json_error.text);
- ovsdb_disconnect (self);
- return;
- }
- ovsdb_got_update (self, params);
- return;
- }
-
- /* This is a message we are not interested in. */
- _LOGD ("got an unknown message, ignoring");
-}
-
-/*****************************************************************************/
-
-/* Lower level marshalling and demarshalling of the JSON-RPC traffic on the
- * ovsdb socket. */
-
-static size_t
-_json_callback (void *buffer, size_t buflen, void *user_data)
-{
- NMOpenvswitchFactory *self = NM_OPENVSWITCH_FACTORY (user_data);
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
-
- if (priv->bufp == priv->input->len) {
- /* No more bytes buffered for decoding. */
- return 0;
- }
-
- /* Pass one more byte to the JSON decoder. */
- *(char *)buffer = priv->input->str[priv->bufp];
- priv->bufp++;
-
- return (size_t)1;
-}
-
-/**
- * ovsdb_read_cb:
- *
- * Read out the data available from the ovsdb socket and try to deserialize
- * the JSON. If we see a complete object, pass it upwards to ovsdb_got_msg().
- */
-static void
-ovsdb_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
- NMOpenvswitchFactory *self = NM_OPENVSWITCH_FACTORY (user_data);
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- GInputStream *stream = G_INPUT_STREAM (source_object);
- GError *error = NULL;
- gssize size;
- json_t *msg;
- json_error_t json_error = { 0, };
-
- size = g_input_stream_read_finish (stream, res, &error);
- if (size == -1) {
- _LOGW ("short read from ovsdb: %s", error->message);
- g_clear_error (&error);
- ovsdb_disconnect (self);
- return;
- }
-
- g_string_append_len (priv->input, priv->buf, size);
- do {
- priv->bufp = 0;
- /* The callback always eats up only up to a single byte. This makes
- * it possible for us to identify complete JSON objects in spite of
- * us not knowing the length in advance. */
- msg = json_load_callback (_json_callback, self, JSON_DISABLE_EOF_CHECK, &json_error);
- if (msg) {
- ovsdb_got_msg (self, msg);
- g_string_erase (priv->input, 0, priv->bufp);
- }
- json_decref (msg);
- } while (msg);
-
- if (size)
- ovsdb_read (self);
-}
-
-static void
-ovsdb_read (NMOpenvswitchFactory *self)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
-
- g_input_stream_read_async (g_io_stream_get_input_stream (G_IO_STREAM (priv->conn)),
- priv->buf, sizeof(priv->buf),
- G_PRIORITY_DEFAULT, NULL, ovsdb_read_cb, self);
-}
-
-static void
-ovsdb_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
- GOutputStream *stream = G_OUTPUT_STREAM (source_object);
- NMOpenvswitchFactory *self = NM_OPENVSWITCH_FACTORY (user_data);
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- GError *error = NULL;
- gssize size;
-
- size = g_output_stream_write_finish (stream, res, &error);
- if (size == -1) {
- _LOGW ("short write to ovsdb: %s", error->message);
- g_clear_error (&error);
- ovsdb_disconnect (self);
- return;
- }
-
- g_string_erase (priv->output, 0, size);
-
- ovsdb_write (self);
-}
-
-static void
-ovsdb_write (NMOpenvswitchFactory *self)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- GOutputStream *stream;
-
- if (!priv->output->len)
- return;
-
- stream = g_io_stream_get_output_stream (G_IO_STREAM (priv->conn));
- if (g_output_stream_has_pending (stream))
- return;
-
- g_output_stream_write_async (stream,
- priv->output->str, priv->output->len,
- G_PRIORITY_DEFAULT, NULL, ovsdb_write_cb, self);
-}
-/*****************************************************************************/
-
-/* Routines to maintain the ovsdb connection. */
-
-/**
- * ovsdb_disconnect:
- *
- * Clean up the internal state to the point equivalent to before connecting.
- * Apart from clean shutdown this is a good response to unexpected trouble,
- * since the next method call attempt a will trigger reconnect which hopefully
- * puts us back in sync.
- */
-static void
-ovsdb_disconnect (NMOpenvswitchFactory *self)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- OvsdbMethodCall *call;
- GError *error;
-
- _LOGD ("disconnecting from ovsdb");
-
- while (priv->calls->len) {
- error = NULL;
- call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1);
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
- call->callback (self, NULL, error, call->user_data);
- g_array_remove_index (priv->calls, priv->calls->len - 1);
- }
-
- g_string_truncate (priv->input, 0);
- g_string_truncate (priv->output, 0);
- g_clear_object (&priv->client);
- g_clear_object (&priv->conn);
- g_clear_pointer (&priv->db_uuid, g_free);
-}
-
-static void
-_monitor_bridges_cb (NMOpenvswitchFactory *self, json_t *response, GError *error, gpointer user_data)
-{
- json_t *result = NULL;
- json_error_t json_error = { 0, };
-
- if (error) {
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- _LOGI ("%s", error->message);
- ovsdb_disconnect (self);
- }
-
- g_clear_error (&error);
- return;
- }
-
- if (json_unpack_ex (response, &json_error, 0, "{s:o}",
- "result", &result) == -1) {
- _LOGW ("monitor_bridges finished with no result: %s", json_error.text);
- ovsdb_disconnect (self);
- return;
- }
-
- /* Treat the first response the same as the subsequent "update"
- * messages we eventually get. */
- ovsdb_got_update (self, result);
-}
-
-static void
-_client_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
- GSocketClient *client = G_SOCKET_CLIENT (source_object);
- NMOpenvswitchFactory *self = NM_OPENVSWITCH_FACTORY (user_data);
- NMOpenvswitchFactoryPrivate *priv;
- GError *error = NULL;
- GSocketConnection *conn;
-
- conn = g_socket_client_connect_finish (client, res, &error);
- if (conn == NULL) {
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
- _LOGI ("%s", error->message);
-
- ovsdb_disconnect (self);
- g_clear_error (&error);
- return;
- }
-
- priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- priv->conn = conn;
- g_clear_object (&priv->cancellable);
-
- ovsdb_read (self);
- ovsdb_next_command (self);
-}
-
-/**
- * ovsdb_try_connect:
- *
- * Establish a connection to ovsdb unless it's already established or being
- * established. Queues a monitor command as a very first one so that we're in
- * sync when other commands are issued.
- */
-static void
-ovsdb_try_connect (NMOpenvswitchFactory *self)
-{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
- GSocketAddress *addr;
-
- if (priv->client)
- return;
-
- /* XXX: This should probably be made configurable via NetworkManager.conf */
- addr = g_unix_socket_address_new (RUNSTATEDIR "/openvswitch/db.sock");
-
- priv->client = g_socket_client_new ();
- priv->cancellable = g_cancellable_new ();
- g_socket_client_connect_async (priv->client, G_SOCKET_CONNECTABLE (addr),
- priv->cancellable, _client_connect_cb, self);
- g_object_unref (addr);
-
- /* Queue a monitor call before any other command, ensuring that we have an up
- * to date view of existing bridged that we need for add and remove ops. */
- ovsdb_call_method (self, OVSDB_MONITOR, NULL, NULL, _monitor_bridges_cb, NULL);
-}
-
-/*****************************************************************************/
-
-/* Public functions useful for NMDeviceOpenvswitch to maintain the life cycle of
- * their ovsdb entries without having to deal with ovsdb complexities themselves. */
-
-typedef struct {
- NMOpenvswitchFactoryCallback callback;
- gpointer user_data;
-} OpenvswitchFactoryCall;
-
-static void
-_transact_cb (NMOpenvswitchFactory *self, json_t *response, GError *error, gpointer user_data)
-{
- OpenvswitchFactoryCall *call = user_data;
- json_error_t json_error = { 0, };
- const char *err;
- const char *err_details;
- json_t *result;
- size_t index;
- json_t *value;
-
- if (error)
- goto out;
-
- if (json_unpack_ex (response, &json_error, 0, "{s:o}", "result", &result) == -1) {
- g_set_error (&error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
- "Bad response from ovsdb: %s", json_error.text);
- goto out;
- }
-
- json_array_foreach (result, index, value) {
- if (json_unpack (value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
- g_set_error (&error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
- "Error running the transaction: %s: %s", err, err_details);
- goto out;
- }
- }
-
-out:
- call->callback (error, call->user_data);
- g_slice_free (OpenvswitchFactoryCall, call);
-}
-
-static void
-_transact_call (NMOpenvswitchFactory *self, OvsdbCommand command,
- const char *bridge_iface, const char *port_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data)
-{
- OpenvswitchFactoryCall *call;
-
- call = g_slice_new (OpenvswitchFactoryCall);
- call->callback = callback;
- call->user_data = user_data;
-
- ovsdb_call_method (self, command, bridge_iface, port_iface, _transact_cb, call);
-}
-
-void
-nm_openvswitch_factory_add_br (NMOpenvswitchFactory *self, const char *bridge_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data)
-{
- _transact_call (self, OVSDB_ADD_BR, bridge_iface, NULL,
- callback, user_data);
-}
-
-void
-nm_openvswitch_factory_del_br (NMOpenvswitchFactory *self, const char *bridge_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data)
-{
- _transact_call (self, OVSDB_DEL_BR, bridge_iface, NULL,
- callback, user_data);
-}
-
-void
-nm_openvswitch_factory_add_port (NMOpenvswitchFactory *self,
- const char *bridge_iface, const char *port_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data)
-{
- _transact_call (self, OVSDB_ADD_PORT, bridge_iface, port_iface,
- callback, user_data);
-}
-
-void
-nm_openvswitch_factory_del_port (NMOpenvswitchFactory *self,
- const char *bridge_iface, const char *port_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data)
-{
- _transact_call (self, OVSDB_DEL_PORT, bridge_iface, port_iface,
- callback, user_data);
-}
-
-/*****************************************************************************/
-
NM_DEVICE_FACTORY_DECLARE_TYPES (
NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH)
- NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_OPENVSWITCH_SETTING_NAME)
+ NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+ NM_SETTING_OVS_PORT_SETTING_NAME)
)
G_MODULE_EXPORT NMDeviceFactory *
@@ -913,9 +67,7 @@ nm_device_factory_create (GError **error)
static void
start (NMDeviceFactory *factory)
{
- NMOpenvswitchFactory *self = NM_OPENVSWITCH_FACTORY (factory);
-
- ovsdb_try_connect (self);
+ nm_ovsdb_get ();
}
static NMDevice *
@@ -937,69 +89,15 @@ create_device (NMDeviceFactory *factory,
}
static void
-_free_port (gpointer data)
-{
- OpenvswitchPort *ovs_port = data;
-
- g_ptr_array_free (ovs_port->interfaces, TRUE);
- g_slice_free (OpenvswitchPort, ovs_port);
-}
-
-static void
-_free_bridge (gpointer data)
-{
- OpenvswitchBridge *ovs_bridge = data;
-
- g_ptr_array_free (ovs_bridge->ports, TRUE);
- g_slice_free (OpenvswitchBridge, ovs_bridge);
-}
-
-static void
nm_openvswitch_factory_init (NMOpenvswitchFactory *self)
{
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
-
- priv->calls = g_array_new (FALSE, TRUE, sizeof (OvsdbMethodCall));
- priv->input = g_string_new (NULL);
- priv->output = g_string_new (NULL);
- priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _free_port);
- priv->bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _free_bridge);
-}
-
-static void
-dispose (GObject *object)
-{
- NMOpenvswitchFactory *self = NM_OPENVSWITCH_FACTORY (object);
- NMOpenvswitchFactoryPrivate *priv = NM_OPENVSWITCH_FACTORY_GET_PRIVATE (self);
-
- ovsdb_disconnect (self);
-
- g_string_free (priv->input, TRUE);
- priv->input = NULL;
- g_string_free (priv->output, TRUE);
- priv->output = NULL;
-
- g_array_free (priv->calls, TRUE);
- priv->calls = NULL;
-
- g_clear_pointer (&priv->ports, g_hash_table_destroy);
- g_clear_pointer (&priv->bridges, g_hash_table_destroy);
-
- g_cancellable_cancel (priv->cancellable);
- g_clear_object (&priv->cancellable);
-
- /* Chain up to the parent class */
- G_OBJECT_CLASS (nm_openvswitch_factory_parent_class)->dispose (object);
}
static void
nm_openvswitch_factory_class_init (NMOpenvswitchFactoryClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass);
- object_class->dispose = dispose;
-
factory_class->get_supported_types = get_supported_types;
factory_class->start = start;
factory_class->create_device = create_device;
diff --git a/src/devices/openvswitch/nm-openvswitch-factory.h b/src/devices/openvswitch/nm-openvswitch-factory.h
deleted file mode 100644
index dbc8db84d..000000000
--- a/src/devices/openvswitch/nm-openvswitch-factory.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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.
- */
-
-#ifndef __NETWORKMANAGER_OPENVSWITCH_FACTORY_H__
-#define __NETWORKMANAGER_OPENVSWITCH_FACTORY_H__
-
-#include "devices/nm-device-factory.h"
-
-#define NM_TYPE_OPENVSWITCH_FACTORY (nm_openvswitch_factory_get_type ())
-#define NM_OPENVSWITCH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OPENVSWITCH_FACTORY, NMOpenvswitchFactory))
-#define NM_OPENVSWITCH_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OPENVSWITCH_FACTORY, NMOpenvswitchFactoryClass))
-#define NM_IS_OPENVSWITCH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OPENVSWITCH_FACTORY))
-#define NM_IS_OPENVSWITCH_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OPENVSWITCH_FACTORY))
-#define NM_OPENVSWITCH_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OPENVSWITCH_FACTORY, NMOpenvswitchFactoryClass))
-
-typedef struct _NMOpenvswitchFactory NMOpenvswitchFactory;
-typedef struct _NMOpenvswitchFactoryClass NMOpenvswitchFactoryClass;
-
-typedef void (*NMOpenvswitchFactoryCallback)(GError *error, gpointer user_data);
-
-GType nm_openvswitch_factory_get_type (void);
-
-void nm_openvswitch_factory_add_br (NMOpenvswitchFactory *self, const char *iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data);
-
-void nm_openvswitch_factory_del_br (NMOpenvswitchFactory *self, const char *iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data);
-
-void nm_openvswitch_factory_add_port (NMOpenvswitchFactory *self,
- const char *bridge_iface, const char *port_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data);
-
-void nm_openvswitch_factory_del_port (NMOpenvswitchFactory *self,
- const char *bridge_iface, const char *port_iface,
- NMOpenvswitchFactoryCallback callback, gpointer user_data);
-
-#endif /* __NETWORKMANAGER_OPENVSWITCH_FACTORY_H__ */
diff --git a/src/devices/openvswitch/nm-ovsdb.c b/src/devices/openvswitch/nm-ovsdb.c
new file mode 100644
index 000000000..c70038811
--- /dev/null
+++ b/src/devices/openvswitch/nm-ovsdb.c
@@ -0,0 +1,1203 @@
+/* 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 (C) 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-ovsdb.h"
+
+#include <string.h>
+#include <jansson.h>
+#include <gmodule.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "nm-device-openvswitch.h"
+#include "platform/nm-platform.h"
+#include "nm-core-internal.h"
+
+/*****************************************************************************/
+
+typedef struct {
+ char *name;
+ GPtrArray *interfaces; /* interface uuids */
+} OpenvswitchPort;
+
+typedef struct {
+ char *name;
+ GPtrArray *ports; /* port uuids */
+} OpenvswitchBridge;
+
+typedef struct {
+ GSocketClient *client;
+ GSocketConnection *conn;
+ GCancellable *cancellable;
+ char buf[4096]; /* Input buffer */
+ size_t bufp; /* Last decoded byte in the input buffer. */
+ GString *input; /* JSON stream waiting for decoding. */
+ GString *output; /* JSON stream to be sent. */
+ gint64 seq;
+ GArray *calls; /* Method calls waiting for a response. */
+ GHashTable *interfaces; /* interface uuid => interface name */
+ GHashTable *ports; /* port uuid => OpenvswitchBridge */
+ GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */
+ const char *db_uuid;
+} NMOvsdbPrivate;
+
+struct _NMOvsdb {
+ GObject parent;
+ NMOvsdbPrivate _priv;
+};
+
+struct _NMOvsdbClass {
+ GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMOvsdb, nm_ovsdb, G_TYPE_OBJECT)
+
+#define NM_OVSDB_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMOvsdb, NM_IS_OVSDB)
+
+#define _NMLOG_DOMAIN LOGD_DEVICE
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovsdb", __VA_ARGS__)
+
+NM_DEFINE_SINGLETON_GETTER (NMOvsdb, nm_ovsdb_get, NM_TYPE_OVSDB);
+
+/*****************************************************************************/
+
+static void ovsdb_try_connect (NMOvsdb *self);
+static void ovsdb_disconnect (NMOvsdb *self);
+static void ovsdb_read (NMOvsdb *self);
+static void ovsdb_write (NMOvsdb *self);
+static void ovsdb_next_command (NMOvsdb *self);
+
+/*****************************************************************************/
+
+/* ovsdb command abstraction. */
+
+typedef void (*OvsdbMethodCallback) (NMOvsdb *self, json_t *response,
+ GError *error, gpointer user_data);
+
+typedef enum {
+ OVSDB_MONITOR,
+ OVSDB_ADD_BR,
+ OVSDB_DEL_BR,
+ OVSDB_ADD_PORT,
+ OVSDB_DEL_PORT,
+} OvsdbCommand;
+
+typedef struct {
+ gint64 id;
+#define COMMAND_PENDING -1 /* id not yet assigned */
+ OvsdbMethodCallback callback;
+ gpointer user_data;
+ OvsdbCommand command;
+ char bridge[IFNAMSIZ + 1];
+ char port[IFNAMSIZ + 1];
+ char iface[IFNAMSIZ + 1];
+} OvsdbMethodCall;
+
+static void
+_call_trace (const char *comment, OvsdbMethodCall *call)
+{
+#ifdef NM_MORE_LOGGING
+ const char *op = NULL;
+
+ switch (call->command) {
+ case OVSDB_MONITOR:
+ op = "monitor";
+ break;
+ case OVSDB_ADD_BR:
+ op = "add-br";
+ break;
+ case OVSDB_DEL_BR:
+ op = "del-br";
+ break;
+ case OVSDB_ADD_PORT:
+ op = "add-port";
+ break;
+ case OVSDB_DEL_PORT:
+ op = "del-port";
+ break;
+ }
+
+ _LOGT ("%s: %s (bridge=%s port=%s interface=%s)", comment, op,
+ call->bridge, call->port, call->iface);
+
+ g_return_if_fail (op);
+#endif
+}
+
+/**
+ * ovsdb_call_method:
+ *
+ * Queues the ovsdb command. Eventually fires the command right away if
+ * there's no command pending completion.
+ */
+static void
+ovsdb_call_method (NMOvsdb *self, OvsdbCommand command,
+ const char *bridge, const char *port, const char *iface,
+ OvsdbMethodCallback callback, gpointer user_data)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ OvsdbMethodCall *call;
+ int copied;
+
+ /* Ensure we're not unsynchronized before we queue the method call. */
+ ovsdb_try_connect (self);
+
+ g_array_set_size (priv->calls, priv->calls->len + 1);
+ call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1);
+ call->id = COMMAND_PENDING;
+ call->command = command;
+ if (bridge) {
+ copied = g_strlcpy (call->bridge, bridge, sizeof (call->bridge));
+ g_return_if_fail (copied < sizeof (call->bridge));
+ }
+ if (port) {
+ copied = g_strlcpy (call->port, port, sizeof (call->port));
+ g_return_if_fail (copied < sizeof (call->port));
+ }
+ if (iface) {
+ copied = g_strlcpy (call->iface, iface, sizeof (call->iface));
+ g_return_if_fail (copied < sizeof (call->iface));
+ }
+ call->callback = callback;
+ call->user_data = user_data;
+
+ _call_trace ("call", call);
+
+ ovsdb_next_command (self);
+}
+
+/*****************************************************************************/
+
+/* Create and process the JSON-RPC messages from ovsdb. */
+
+/**
+ * _fill_bridges:
+ *
+ * Put set of all bridges into @items and all but @exclude_bridge into
+ * @new_items. The array with the ommited element is useful for replacement
+ * or deletion while the full array is good for ensuring the database is
+ * in the state we expect it to be prior to the transaction.
+ */
+static void
+_fill_bridges (NMOvsdb *self, const char *exclude_bridge,
+ json_t **items, json_t **new_items)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GHashTableIter iter;
+ char *bridge_uuid;
+ OpenvswitchBridge *ovs_bridge;
+
+ *items = json_array ();
+ *new_items = json_array ();
+
+ g_hash_table_iter_init (&iter, priv->bridges);
+ while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) {
+ json_array_append_new (*items, json_pack ("[s,s]", "uuid", bridge_uuid));
+ if (g_strcmp0 (exclude_bridge, ovs_bridge->name) != 0)
+ json_array_append_new (*new_items, json_pack ("[s,s]", "uuid", bridge_uuid));
+ }
+}
+
+/**
+ * _expect_bridges:
+ *
+ * Return a command that will fail the transaction if the actual set of
+ * bridges doesn't match @bridges. This is a way of detecting race conditions
+ * with other ovsdb clients that might be adding or removing bridges
+ * at the same time.
+ */
+static json_t *
+_expect_bridges (json_t *bridges, const char *db_uuid)
+{
+ return json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, o]}], s:[[s, s, [s, s]]]}",
+ "op", "wait", "table", "Open_vSwitch",
+ "timeout", 0, "columns", "bridges",
+ "until", "==", "rows", "bridges", "set", bridges,
+ "where", "_uuid", "==", "uuid", db_uuid);
+}
+
+/**
+ * _set_bridges:
+ *
+ * Return a command that will update the list of bridges in @db_uuid
+ * database to @bridges.
+ */
+static json_t *
+_set_bridges (const json_t *bridges, const char *db_uuid)
+{
+ return json_pack ("{s:s, s:s, s:{s:[s, o]}, s:[[s, s, [s, s]]]}",
+ "op", "update", "table", "Open_vSwitch",
+ "row", "bridges", "set", bridges,
+ "where", "_uuid", "==", "uuid", db_uuid);
+}
+
+/*
+ * _fill_ports:
+ *
+ * Put set of all ports of @bridge into @items and all but @exclude_port into
+ * @new_items.
+ *
+ * Returns: %TRUE if the specified port was actually seen, helping us to decide
+ * whether we need to put an itnerface into a new one or update the
+ * existing one.
+ */
+static gboolean
+_fill_ports (NMOvsdb *self,
+ const char *bridge, const char *exclude_port,
+ json_t **items, json_t **new_items)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GHashTableIter iter;
+ char *bridge_uuid;
+ char *port_uuid;
+ OpenvswitchBridge *ovs_bridge;
+ OpenvswitchPort *ovs_port;
+ gboolean found = FALSE;
+ int i;
+
+ *items = json_array ();
+ *new_items = json_array ();
+
+ g_hash_table_iter_init (&iter, priv->bridges);
+ while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) {
+ if (g_strcmp0 (ovs_bridge->name, bridge) != 0)
+ continue;
+ for (i = 0; i < ovs_bridge->ports->len; i++) {
+ port_uuid = g_ptr_array_index (ovs_bridge->ports, i);
+ json_array_append_new (*items, json_pack ("[s,s]", "uuid", port_uuid));
+
+ ovs_port = g_hash_table_lookup (priv->ports, port_uuid);
+ if (!ovs_port)
+ continue;
+ if (g_strcmp0 (exclude_port, ovs_port->name) == 0) {
+ found = TRUE;
+ continue;
+ }
+ json_array_append_new (*new_items, json_pack ("[s,s]", "uuid", port_uuid));
+ }
+ }
+
+ return found;
+}
+
+/**
+ * _expect_ports:
+ *
+ * Return a command that will fail the transaction if the actual set of
+ * ports in @bridge doesn't match @ports. This is a way of detecting
+ * race conditions with other ovsdb clients that might be adding or removing
+ * bridge ports at the same time.
+ */
+static json_t *
+_expect_ports (const char *bridge, const json_t *ports)
+{
+ return json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, o]}], s:[[s, s, s]]}",
+ "op", "wait", "table", "Bridge",
+ "timeout", 0, "columns", "ports",
+ "until", "==", "rows", "ports", "set", ports,
+ "where", "name", "==", bridge);
+}
+
+/**
+ * _set_ports:
+ *
+ * Return a command that will update the list of ports of @bridge
+ * to @ports.
+ */
+static json_t *
+_set_ports (const char *bridge, const json_t *ports)
+{
+ return json_pack ("{s:s, s:s, s:{s:[s, o]}, s:[[s, s, s]]}",
+ "op", "update", "table", "Bridge",
+ "row", "ports", "set", ports,
+ "where", "name", "==", bridge);
+}
+
+/*
+ * _fill_interfaces:
+ *
+ * Put set of all interfaces of @port into @items and all but
+ * @exclude_iface into @new_items.
+ */
+static void
+_fill_interfaces (NMOvsdb *self,
+ const char *port, const char *exclude_iface,
+ json_t **items, json_t **new_items)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GHashTableIter iter;
+ char *port_uuid;
+ char *iface_uuid;
+ OpenvswitchPort *ovs_port;
+ const char *ovs_iface;
+ int i;
+
+ *items = json_array ();
+ *new_items = json_array ();
+
+ g_hash_table_iter_init (&iter, priv->ports);
+ while (g_hash_table_iter_next (&iter, (gpointer) &port_uuid, (gpointer) &ovs_port)) {
+ if (g_strcmp0 (ovs_port->name, port) != 0)
+ continue;
+ for (i = 0; i < ovs_port->interfaces->len; i++) {
+ iface_uuid = g_ptr_array_index (ovs_port->interfaces, i);
+ json_array_append_new (*items, json_pack ("[s,s]", "uuid", iface_uuid));
+
+ ovs_iface = g_hash_table_lookup (priv->interfaces, iface_uuid);
+ if (!ovs_iface)
+ continue;
+ if (g_strcmp0 (exclude_iface, ovs_iface) == 0)
+ continue;
+ json_array_append_new (*new_items, json_pack ("[s,s]", "uuid", iface_uuid));
+ }
+ }
+}
+
+/**
+ * _expect_interfaces:
+ *
+ * Return a command that will fail the transaction if the actual set of
+ * interfaces in @port doesn't match @interfaces. This is a way of detecting
+ * race conditions with other ovsdb clients that might be adding or removing
+ * port interfaces at the same time.
+ */
+static json_t *
+_expect_interfaces (const char *port, const json_t *interfaces)
+{
+ return json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, o]}], s:[[s, s, s]]}",
+ "op", "wait", "table", "Port",
+ "timeout", 0, "columns", "interfaces",
+ "until", "==", "rows", "interfaces", "set", interfaces,
+ "where", "name", "==", port);
+}
+
+/**
+ * _set_interfaces:
+ *
+ * Return a command that will update the list of interfaces of @port
+ * to @interfaces.
+ */
+static json_t *
+_set_interfaces (const char *port, const json_t *interfaces)
+{
+ return json_pack ("{s:s, s:s, s:{s:[s, o]}, s:[[s, s, s]]}",
+ "op", "update", "table", "Port",
+ "row", "interfaces", "set", interfaces,
+ "where", "name", "==", port);
+}
+
+/**
+ * _inc_next_cfg:
+ *
+ * Returns an mutate commands that bumps next_cfg upon successful completion
+ * of the transaction it is in.
+ */
+static json_t *
+_inc_next_cfg (const char *db_uuid)
+{
+ return json_pack ("{s:s, s:s, s:[[s, s, i]], s:[[s, s, [s, s]]]}",
+ "op", "mutate", "table", "Open_vSwitch",
+ "mutations", "next_cfg", "+=", 1,
+ "where", "_uuid", "==", "uuid", db_uuid);
+}
+
+/**
+ * ovsdb_next_command:
+ *
+ * Translates a higher level operation (add/remove bridge/port) to a RFC 7047
+ * command serialized into JSON ands sends it over to the database.
+
+ * Only called when no command is waiting for a response, since the serialized
+ * command might depend on result of a previous one (add and remove need to
+ * include an up to date bridge list in their transactions to rule out races).
+ */
+static void
+ovsdb_next_command (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ OvsdbMethodCall *call = NULL;
+ char *cmd;
+ json_t *msg = NULL;
+ json_t *items, *new_items;
+ json_t *params;
+
+ if (!priv->conn)
+ return;
+ if (!priv->calls->len)
+ return;
+ call = &g_array_index (priv->calls, OvsdbMethodCall, 0);
+ if (call->id != COMMAND_PENDING)
+ return;
+ call->id = priv->seq++;
+
+ switch (call->command) {
+ case OVSDB_MONITOR:
+ msg = json_pack ("{s:i, s:s, s:[s, n, {"
+ " s:[{s:[s, s]}],"
+ " s:[{s:[s, s]}],"
+ " s:[{s:[s]}],"
+ " s:[{s:[]}]"
+ "}]}",
+ "id", call->id,
+ "method", "monitor", "params", "Open_vSwitch",
+ "Bridge", "columns", "name", "ports",
+ "Port", "columns", "name", "interfaces",
+ "Interface", "columns", "name",
+ "Open_vSwitch", "columns");
+ break;
+ case OVSDB_ADD_BR:
+ _fill_bridges (self, call->bridge, &items, &new_items);
+ json_array_append_new (new_items, json_pack ("[s,s]", "named-uuid", "rowBridge"));
+
+ msg = json_pack ("{s:i, s:s, s:[s,o,o,o"
+ " {s:s, s:s, s:{s:s, s:s}, s:s}, " /* insert interface */
+ " {s:s, s:s, s:{s:s, s:[s, s]}, s:s}," /* insert port */
+ " {s:s, s:s, s:{s:s, s:[s, s]}, s:s}," /* insert bridge */
+ "]}",
+ "id", call->id,
+ "method", "transact", "params", "Open_vSwitch",
+ _expect_bridges (items, priv->db_uuid),
+ _set_bridges (new_items, priv->db_uuid),
+ _inc_next_cfg (priv->db_uuid),
+ "op", "insert", "table", "Interface", "row", "name", call->bridge,
+ "type", "internal", "uuid-name", "rowIntf",
+ "op", "insert", "table", "Port", "row", "name", call->bridge,
+ "interfaces", "named-uuid", "rowIntf", "uuid-name", "rowPort",
+ "op", "insert", "table", "Bridge", "row", "name", call->bridge,
+ "ports", "named-uuid", "rowPort", "uuid-name", "rowBridge");
+ break;
+ case OVSDB_DEL_BR:
+ _fill_bridges (self, call->bridge, &items, &new_items);
+
+ msg = json_pack ("{s:i, s:s, s:[s,o,o,o]}",
+ "id", call->id,
+ "method", "transact", "params", "Open_vSwitch",
+ _expect_bridges (items, priv->db_uuid),
+ _set_bridges (new_items, priv->db_uuid),
+ _inc_next_cfg (priv->db_uuid));
+ break;
+ case OVSDB_ADD_PORT:
+ params = json_array ();
+ json_array_append_new (params, json_string ("Open_vSwitch"));
+
+ /* Insert the new interface. */
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:{s:s}, s:s}",
+ "op", "insert", "table", "Interface", "row", "name", call->iface,
+ "uuid-name", "rowIntf"));
+
+ if (_fill_ports (self, call->bridge, call->port, &items, &new_items)) {
+ /* The port exists, update it with the new interface. */
+ json_decref (items);
+ json_decref (new_items);
+ _fill_interfaces (self, call->port, call->iface, &items, &new_items);
+ json_array_append_new (new_items, json_pack ("[s,s]", "named-uuid", "rowIntf"));
+ json_array_append_new (params, _expect_interfaces (call->port, items));
+ json_array_append_new (params, _set_interfaces (call->port, new_items));
+ } else {
+ /* Create a new port along with the interface. */
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:{s:s, s:[s, s]}, s:s}",
+ "op", "insert", "table", "Port", "row", "name", call->port,
+ "interfaces", "named-uuid", "rowIntf", "uuid-name", "rowPort"));
+ json_array_append_new (new_items, json_pack ("[s,s]", "named-uuid", "rowPort"));
+ json_array_append_new (params, _expect_ports (call->bridge, items));
+ json_array_append_new (params, _set_ports (call->bridge, new_items));
+ }
+
+ msg = json_pack ("{s:i, s:s, s:o}",
+ "id", call->id,
+ "method", "transact", "params", params);
+
+ break;
+ case OVSDB_DEL_PORT:
+ params = json_array ();
+ json_array_append_new (params, json_string ("Open_vSwitch"));
+
+ _fill_interfaces (self, call->port, call->iface, &items, &new_items);
+ if (json_array_size (new_items) == 0) {
+ /* A port can't exist without interfaces, drop it altogether. */
+ json_decref (items);
+ json_decref (new_items);
+ _fill_ports (self, call->bridge, call->port, &items, &new_items);
+ json_array_append_new (params, _expect_ports (call->bridge, items));
+ json_array_append_new (params, _set_ports (call->bridge, new_items));
+ } else {
+ /* Drop just the interface from the port. */
+ json_array_append_new (params, _expect_interfaces (call->port, items));
+ json_array_append_new (params, _set_interfaces (call->port, new_items));
+ }
+
+ msg = json_pack ("{s:i, s:s, s:o}",
+ "id", call->id,
+ "method", "transact", "params", params);
+ break;
+ }
+
+ g_return_if_fail (msg);
+ cmd = json_dumps (msg, 0);
+
+ g_string_append (priv->output, cmd);
+ json_decref (msg);
+ free (cmd);
+
+ ovsdb_write (self);
+}
+
+/**
+ * _uuids_to_array:
+ *
+ * This tidies up the somewhat non-straightforward way ovsdb represents an array
+ * of UUID elements. The single element is a tuple (called <atom> in RFC7047),
+ *
+ * [ "uuid", "aa095ffb-e1f1-0fc4-8038-82c1ea7e4797" ]
+ *
+ * while the list of multiple UUIDs are turned into a set of such tuples ("atoms"):
+ *
+ * [ "set", [ [ "uuid", "aa095ffb-e1f1-0fc4-8038-82c1ea7e4797" ],
+ * [ "uuid", "185c93f6-0b39-424e-8587-77d074aa7ce0" ], ... ] ]
+ */
+static void
+_uuids_to_array (GPtrArray *array, const json_t *items)
+{
+ const char *key;
+ json_t *value;
+ size_t index = 0;
+ json_t *set_value;
+ size_t set_index;
+
+ while (index < json_array_size (items)) {
+ key = json_string_value (json_array_get (items, index));
+ index++;
+ value = json_array_get (items, index);
+ index++;
+
+ if (!value)
+ return;
+
+ if (g_strcmp0 (key, "uuid") == 0 && json_is_string (value)) {
+ g_ptr_array_add (array, g_strdup (json_string_value (value)));
+ } else if (g_strcmp0 (key, "set") == 0 && json_is_array (value)) {
+ json_array_foreach (value, set_index, set_value) {
+ _uuids_to_array (array, set_value);
+ }
+ }
+ }
+}
+
+/**
+ * ovsdb_got_update:
+ *
+ * Called when we've got an "update" method call (we asked for it with the monitor
+ * command). We use it to maintain a consistent view of bridge list regardless of
+ * whether the changes are done by us or externally.
+ */
+static void
+ovsdb_got_update (NMOvsdb *self, json_t *msg)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ json_t *ovs = NULL;
+ json_t *bridge = NULL;
+ json_t *port = NULL;
+ json_t *interface = NULL;
+ json_t *items;
+ json_error_t json_error = { 0, };
+ void *iter;
+ const char *name;
+ const char *key;
+ json_t *value;
+ OpenvswitchBridge *ovs_bridge;
+ OpenvswitchPort *ovs_port;
+
+ if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:o, s?:o, s?:o}",
+ "Open_vSwitch", &ovs,
+ "Bridge", &bridge,
+ "Port", &port,
+ "Interface", &interface) == -1) {
+ /* This doesn't really have to be an error; the key might
+ * be missing if there really are no bridges present. */
+ _LOGD ("Bad update: %s", json_error.text);
+ }
+
+ if (ovs) {
+ iter = json_object_iter (ovs);
+ priv->db_uuid = g_strdup (iter ? json_object_iter_key (iter) : NULL);
+ }
+
+ json_object_foreach (bridge, key, value) {
+ if (json_unpack (value, "{s:{}}", "old") == 0) {
+ _LOGT ("removed a bridge: %s", name);
+ g_hash_table_remove (priv->bridges, key);
+ }
+ if (json_unpack (value, "{s:{s?:s, s?:o}}", "new", "name", &name, "ports", &items) == 0) {
+ _LOGT ("added a bridge: %s", name);
+ ovs_bridge = g_slice_new (OpenvswitchBridge);
+ ovs_bridge->name = g_strdup (name);
+ ovs_bridge->ports = g_ptr_array_new_with_free_func (g_free);
+ _uuids_to_array (ovs_bridge->ports, items);
+ g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge);
+ }
+ }
+
+ json_object_foreach (port, key, value) {
+ if (json_unpack (value, "{s:{}}", "old") == 0) {
+ _LOGT ("removed a port: %s", name);
+ g_hash_table_remove (priv->ports, key);
+ }
+ if (json_unpack (value, "{s:{s?:s, s?:o}}", "new", "name", &name, "interfaces", &items) == 0) {
+ _LOGT ("added a port: %s", name);
+ ovs_port = g_slice_new (OpenvswitchPort);
+ ovs_port->name = g_strdup (name);
+ ovs_port->interfaces = g_ptr_array_new_with_free_func (g_free);
+ _uuids_to_array (ovs_port->interfaces, items);
+ g_hash_table_insert (priv->ports, g_strdup (key), ovs_port);
+ }
+ }
+
+ json_object_foreach (interface, key, value) {
+ if (json_unpack (value, "{s:{}}", "old") == 0) {
+ _LOGT ("removed a interface: %s", name);
+ g_hash_table_remove (priv->interfaces, key);
+ }
+ if (json_unpack (value, "{s:{s?:s}}", "new", "name", &name) == 0) {
+ _LOGT ("added a interface: %s", name);
+ g_hash_table_insert (priv->interfaces, g_strdup (key), g_strdup (name));
+ }
+ }
+}
+
+/**
+ * ovsdb_got_echo:
+ *
+ * Only implemented because the specification mandates it. Actual ovsdb hasn't been
+ * seen doing this.
+ */
+static void
+ovsdb_got_echo (NMOvsdb *self, json_int_t id, json_t *data)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ json_t *msg;
+ char *reply;
+ gboolean output_was_empty;
+
+ output_was_empty = priv->output->len == 0;
+
+ msg = json_pack ("{s:I, s:O}", "id", id, "result", data);
+ reply = json_dumps (msg, 0);
+ g_string_append (priv->output, reply);
+ json_decref (msg);
+ free (reply);
+
+ if (output_was_empty)
+ ovsdb_write (self);
+}
+
+/**
+ * ovsdb_got_msg::
+ *
+ * Called when when a complete JSON object was seen and unmarshalled.
+ * Either finishes a method call or processes a method call.
+ */
+static void
+ovsdb_got_msg (NMOvsdb *self, json_t *msg)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ json_error_t json_error = { 0, };
+ json_t *json_id = NULL;
+ gint64 id = -1;
+ const char *method = NULL;
+ json_t *params = NULL;
+ json_t *result = NULL;
+ json_t *error = NULL;
+ OvsdbMethodCall *call = NULL;
+ OvsdbMethodCallback callback;
+ gpointer user_data;
+ GError *local = NULL;
+
+ if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:s, s?:o, s?:o, s?:o}",
+ "id", &json_id,
+ "method", &method,
+ "params", &params,
+ "result", &result,
+ "error", &error) == -1) {
+ _LOGW ("couldn't grok the message: %s", json_error.text);
+ ovsdb_disconnect (self);
+ return;
+ }
+
+ if (json_is_number (json_id))
+ id = json_integer_value (json_id);
+
+ if (method) {
+ /* It's a method call! */
+ if (!params) {
+ _LOGW ("a method call with no params: '%s'", method);
+ ovsdb_disconnect (self);
+ return;
+ }
+
+ if (g_strcmp0 (method, "update") == 0) {
+ /* This is a update method call. */
+ ovsdb_got_update (self, json_array_get (params, 1));
+ } else if (g_strcmp0 (method, "echo") == 0) {
+ /* This is an echo request. */
+ ovsdb_got_echo (self, id, params);
+ } else {
+ _LOGW ("got an unknown method call: '%s'", method);
+ }
+ return;
+ }
+
+ if (id > -1) {
+ /* This is a response to a method call. */
+ if (!priv->calls->len) {
+ _LOGE ("there are no queued calls expecting response %ld", id);
+ ovsdb_disconnect (self);
+ return;
+ }
+ call = &g_array_index (priv->calls, OvsdbMethodCall, 0);
+ if (call->id != id) {
+ _LOGE ("expected a response to call %ld, not %ld", call->id, id);
+ ovsdb_disconnect (self);
+ return;
+ }
+ /* Cool, we found a corresponsing call. Finish it. */
+
+ _call_trace ("response", call);
+
+ if (!json_is_null (error)) {
+ /* The response contains an error. */
+ g_set_error (&local, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Error call to OVSDB returned an error: %s",
+ json_string_value (error));
+ }
+
+ callback = call->callback;
+ user_data = call->user_data;
+ g_array_remove_index (priv->calls, 0);
+ callback (self, result, local, user_data);
+
+ /* Don't progress further commands in case the callback hit an error
+ * and disconnected us. */
+ if (!priv->conn)
+ return;
+
+ /* Now we're free to serialize and send the next command, if any. */
+ ovsdb_next_command (self);
+
+ return;
+ }
+
+
+ /* This is a message we are not interested in. */
+ _LOGW ("got an unknown message, ignoring");
+}
+
+/*****************************************************************************/
+
+/* Lower level marshalling and demarshalling of the JSON-RPC traffic on the
+ * ovsdb socket. */
+
+static size_t
+_json_callback (void *buffer, size_t buflen, void *user_data)
+{
+ NMOvsdb *self = NM_OVSDB (user_data);
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+
+ if (priv->bufp == priv->input->len) {
+ /* No more bytes buffered for decoding. */
+ return 0;
+ }
+
+ /* Pass one more byte to the JSON decoder. */
+ *(char *)buffer = priv->input->str[priv->bufp];
+ priv->bufp++;
+
+ return (size_t)1;
+}
+
+/**
+ * ovsdb_read_cb:
+ *
+ * Read out the data available from the ovsdb socket and try to deserialize
+ * the JSON. If we see a complete object, pass it upwards to ovsdb_got_msg().
+ */
+static void
+ovsdb_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ NMOvsdb *self = NM_OVSDB (user_data);
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GInputStream *stream = G_INPUT_STREAM (source_object);
+ GError *error = NULL;
+ gssize size;
+ json_t *msg;
+ json_error_t json_error = { 0, };
+
+ size = g_input_stream_read_finish (stream, res, &error);
+ if (size == -1) {
+ _LOGW ("short read from ovsdb: %s", error->message);
+ g_clear_error (&error);
+ ovsdb_disconnect (self);
+ return;
+ }
+
+ g_string_append_len (priv->input, priv->buf, size);
+ do {
+ priv->bufp = 0;
+ /* The callback always eats up only up to a single byte. This makes
+ * it possible for us to identify complete JSON objects in spite of
+ * us not knowing the length in advance. */
+ msg = json_load_callback (_json_callback, self, JSON_DISABLE_EOF_CHECK, &json_error);
+ if (msg) {
+ ovsdb_got_msg (self, msg);
+ g_string_erase (priv->input, 0, priv->bufp);
+ }
+ json_decref (msg);
+ } while (msg);
+
+ if (!priv->conn)
+ return;
+
+ if (size)
+ ovsdb_read (self);
+}
+
+static void
+ovsdb_read (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+
+ g_input_stream_read_async (g_io_stream_get_input_stream (G_IO_STREAM (priv->conn)),
+ priv->buf, sizeof(priv->buf),
+ G_PRIORITY_DEFAULT, NULL, ovsdb_read_cb, self);
+}
+
+static void
+ovsdb_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GOutputStream *stream = G_OUTPUT_STREAM (source_object);
+ NMOvsdb *self = NM_OVSDB (user_data);
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GError *error = NULL;
+ gssize size;
+
+ size = g_output_stream_write_finish (stream, res, &error);
+ if (size == -1) {
+ _LOGW ("short write to ovsdb: %s", error->message);
+ g_clear_error (&error);
+ ovsdb_disconnect (self);
+ return;
+ }
+
+ if (!priv->conn)
+ return;
+
+ g_string_erase (priv->output, 0, size);
+
+ ovsdb_write (self);
+}
+
+static void
+ovsdb_write (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GOutputStream *stream;
+
+ if (!priv->output->len)
+ return;
+
+ stream = g_io_stream_get_output_stream (G_IO_STREAM (priv->conn));
+ if (g_output_stream_has_pending (stream))
+ return;
+
+ g_output_stream_write_async (stream,
+ priv->output->str, priv->output->len,
+ G_PRIORITY_DEFAULT, NULL, ovsdb_write_cb, self);
+}
+/*****************************************************************************/
+
+/* Routines to maintain the ovsdb connection. */
+
+/**
+ * ovsdb_disconnect:
+ *
+ * Clean up the internal state to the point equivalent to before connecting.
+ * Apart from clean shutdown this is a good response to unexpected trouble,
+ * since the next method call attempt a will trigger reconnect which hopefully
+ * puts us back in sync.
+ */
+static void
+ovsdb_disconnect (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ OvsdbMethodCall *call;
+ OvsdbMethodCallback callback;
+ gpointer user_data;
+ GError *error;
+
+ _LOGD ("disconnecting from ovsdb");
+
+ while (priv->calls->len) {
+ error = NULL;
+ call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1);
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
+
+ callback = call->callback;
+ user_data = call->user_data;
+ g_array_remove_index (priv->calls, priv->calls->len - 1);
+ callback (self, NULL, error, user_data);
+ }
+
+ priv->bufp = 0;
+ g_string_truncate (priv->input, 0);
+ g_string_truncate (priv->output, 0);
+ g_clear_object (&priv->client);
+ g_clear_object (&priv->conn);
+ g_clear_pointer (&priv->db_uuid, g_free);
+}
+
+static void
+_monitor_bridges_cb (NMOvsdb *self, json_t *result, GError *error, gpointer user_data)
+{
+ if (error) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ _LOGI ("%s", error->message);
+ ovsdb_disconnect (self);
+ }
+
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Treat the first response the same as the subsequent "update"
+ * messages we eventually get. */
+ ovsdb_got_update (self, result);
+}
+
+static void
+_client_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GSocketClient *client = G_SOCKET_CLIENT (source_object);
+ NMOvsdb *self = NM_OVSDB (user_data);
+ NMOvsdbPrivate *priv;
+ GError *error = NULL;
+ GSocketConnection *conn;
+
+ conn = g_socket_client_connect_finish (client, res, &error);
+ if (conn == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ _LOGI ("%s", error->message);
+
+ ovsdb_disconnect (self);
+ g_clear_error (&error);
+ return;
+ }
+
+ priv = NM_OVSDB_GET_PRIVATE (self);
+ priv->conn = conn;
+ g_clear_object (&priv->cancellable);
+
+ ovsdb_read (self);
+ ovsdb_next_command (self);
+}
+
+/**
+ * ovsdb_try_connect:
+ *
+ * Establish a connection to ovsdb unless it's already established or being
+ * established. Queues a monitor command as a very first one so that we're in
+ * sync when other commands are issued.
+ */
+static void
+ovsdb_try_connect (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GSocketAddress *addr;
+
+ if (priv->client)
+ return;
+
+ /* XXX: This should probably be made configurable via NetworkManager.conf */
+ addr = g_unix_socket_address_new (RUNSTATEDIR "/openvswitch/db.sock");
+
+ priv->client = g_socket_client_new ();
+ priv->cancellable = g_cancellable_new ();
+ g_socket_client_connect_async (priv->client, G_SOCKET_CONNECTABLE (addr),
+ priv->cancellable, _client_connect_cb, self);
+ g_object_unref (addr);
+
+ /* Queue a monitor call before any other command, ensuring that we have an up
+ * to date view of existing bridged that we need for add and remove ops. */
+ ovsdb_call_method (self, OVSDB_MONITOR, NULL, NULL, NULL, _monitor_bridges_cb, NULL);
+}
+
+/*****************************************************************************/
+
+/* Public functions useful for NMDeviceOpenvswitch to maintain the life cycle of
+ * their ovsdb entries without having to deal with ovsdb complexities themselves. */
+
+typedef struct {
+ NMOvsdbCallback callback;
+ gpointer user_data;
+} OvsdbCall;
+
+static void
+_transact_cb (NMOvsdb *self, json_t *result, GError *error, gpointer user_data)
+{
+ OvsdbCall *call = user_data;
+ const char *err;
+ const char *err_details;
+ size_t index;
+ json_t *value;
+
+ if (error)
+ goto out;
+
+ json_array_foreach (result, index, value) {
+ if (json_unpack (value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Error running the transaction: %s: %s", err, err_details);
+ goto out;
+ }
+ }
+
+out:
+ call->callback (error, call->user_data);
+ g_slice_free (OvsdbCall, call);
+}
+
+static void
+_transact_call (NMOvsdb *self, OvsdbCommand command,
+ const char *bridge, const char *port, const char *iface,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ OvsdbCall *call;
+
+ call = g_slice_new (OvsdbCall);
+ call->callback = callback;
+ call->user_data = user_data;
+
+ ovsdb_call_method (self, command, bridge, port, iface, _transact_cb, call);
+}
+
+void
+nm_ovsdb_add_br (NMOvsdb *self, const char *bridge,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ _transact_call (self, OVSDB_ADD_BR, bridge, NULL, NULL,
+ callback, user_data);
+}
+
+void
+nm_ovsdb_del_br (NMOvsdb *self, const char *bridge,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ _transact_call (self, OVSDB_DEL_BR, bridge, NULL, NULL,
+ callback, user_data);
+}
+
+void
+nm_ovsdb_add_port (NMOvsdb *self,
+ const char *bridge, const char *port, const char *iface,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ _transact_call (self, OVSDB_ADD_PORT, bridge, port, iface,
+ callback, user_data);
+}
+
+void
+nm_ovsdb_del_port (NMOvsdb *self,
+ const char *bridge, const char *port, const char *iface,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ _transact_call (self, OVSDB_DEL_PORT, bridge, port, iface,
+ callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+_free_bridge (gpointer data)
+{
+ OpenvswitchBridge *ovs_bridge = data;
+
+ g_ptr_array_free (ovs_bridge->ports, TRUE);
+ g_slice_free (OpenvswitchBridge, ovs_bridge);
+}
+
+static void
+_free_port (gpointer data)
+{
+ OpenvswitchPort *ovs_port = data;
+
+ g_ptr_array_free (ovs_port->interfaces, TRUE);
+ g_slice_free (OpenvswitchPort, ovs_port);
+}
+
+static void
+nm_ovsdb_init (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+
+ priv->calls = g_array_new (FALSE, TRUE, sizeof (OvsdbMethodCall));
+ priv->input = g_string_new (NULL);
+ priv->output = g_string_new (NULL);
+ priv->bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _free_bridge);
+ priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _free_port);
+ priv->interfaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ ovsdb_try_connect (self);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMOvsdb *self = NM_OVSDB (object);
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+
+ ovsdb_disconnect (self);
+
+ g_string_free (priv->input, TRUE);
+ priv->input = NULL;
+ g_string_free (priv->output, TRUE);
+ priv->output = NULL;
+
+ if (priv->calls) {
+ g_array_free (priv->calls, TRUE);
+ priv->calls = NULL;
+ }
+
+ g_clear_pointer (&priv->bridges, g_hash_table_destroy);
+ g_clear_pointer (&priv->ports, g_hash_table_destroy);
+ g_clear_pointer (&priv->interfaces, g_hash_table_destroy);
+
+ g_cancellable_cancel (priv->cancellable);
+ g_clear_object (&priv->cancellable);
+
+ G_OBJECT_CLASS (nm_ovsdb_parent_class)->dispose (object);
+}
+
+static void
+nm_ovsdb_class_init (NMOvsdbClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dispose;
+}
diff --git a/src/devices/openvswitch/nm-ovsdb.h b/src/devices/openvswitch/nm-ovsdb.h
new file mode 100644
index 000000000..83e087970
--- /dev/null
+++ b/src/devices/openvswitch/nm-ovsdb.h
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+#ifndef __NETWORKMANAGER_OVSDB_H__
+#define __NETWORKMANAGER_OVSDB_H__
+
+#define NM_TYPE_OVSDB (nm_ovsdb_get_type ())
+#define NM_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OVSDB, NMOvsdb))
+#define NM_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OVSDB, NMOvsdbClass))
+#define NM_IS_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OVSDB))
+#define NM_IS_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVSDB))
+#define NM_OVSDB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVSDB, NMOvsdbClass))
+
+typedef struct _NMOvsdb NMOvsdb;
+typedef struct _NMOvsdbClass NMOvsdbClass;
+
+typedef void (*NMOvsdbCallback) (GError *error, gpointer user_data);
+
+NMOvsdb *nm_ovsdb_get (void);
+
+GType nm_ovsdb_get_type (void);
+
+void nm_ovsdb_add_br (NMOvsdb *self, const char *bridge,
+ NMOvsdbCallback callback, gpointer user_data);
+
+void nm_ovsdb_del_br (NMOvsdb *self, const char *bridge,
+ NMOvsdbCallback callback, gpointer user_data);
+
+void nm_ovsdb_add_port (NMOvsdb *self,
+ const char *bridge, const char *port, const char *iface,
+ NMOvsdbCallback callback, gpointer user_data);
+
+void nm_ovsdb_del_port (NMOvsdb *self,
+ const char *bridge, const char *port, const char *iface,
+ NMOvsdbCallback callback, gpointer user_data);
+
+#endif /* __NETWORKMANAGER_OVSDB_H__ */