summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2017-10-30 21:48:22 +0100
committerLubomir Rintel <lkundrak@v3.sk>2017-10-30 21:48:22 +0100
commit79168a54344f8fb72fda88bf02bce908d2ce3f31 (patch)
tree9b572f96500061687babbac56ad85220c2aa1ef7
parent7f84f15e069bbaa02426fe8a59bf29a44611adb8 (diff)
parent6b532fed5046d0d658164e5ab9813ce8871cfbeb (diff)
merge: branch 'lr/ovs'
https://bugzilla.redhat.com/show_bug.cgi?id=1470282
-rw-r--r--Makefile.am90
-rw-r--r--clients/cli/connections.c4
-rw-r--r--clients/common/nm-client-utils.c17
-rw-r--r--clients/common/nm-meta-setting-desc.c122
-rw-r--r--clients/common/settings-docs.c.in18
-rw-r--r--configure.ac17
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec23
-rw-r--r--data/NetworkManager-ovs.conf2
-rw-r--r--docs/api/Makefile.am1
-rw-r--r--docs/api/network-manager-docs.xml1
-rw-r--r--introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml20
-rw-r--r--introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml20
-rw-r--r--introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml20
-rw-r--r--libnm-core/nm-connection-private.h6
-rw-r--r--libnm-core/nm-connection.c263
-rw-r--r--libnm-core/nm-connection.h11
-rw-r--r--libnm-core/nm-core-internal.h4
-rw-r--r--libnm-core/nm-core-types.h4
-rw-r--r--libnm-core/nm-dbus-interface.h133
-rw-r--r--libnm-core/nm-setting-connection.c40
-rw-r--r--libnm-core/nm-setting-ip-tunnel.c2
-rw-r--r--libnm-core/nm-setting-ovs-bridge.c337
-rw-r--r--libnm-core/nm-setting-ovs-bridge.h63
-rw-r--r--libnm-core/nm-setting-ovs-interface.c392
-rw-r--r--libnm-core/nm-setting-ovs-interface.h54
-rw-r--r--libnm-core/nm-setting-ovs-patch.c215
-rw-r--r--libnm-core/nm-setting-ovs-patch.h54
-rw-r--r--libnm-core/nm-setting-ovs-port.c471
-rw-r--r--libnm-core/nm-setting-ovs-port.h69
-rw-r--r--libnm-core/nm-setting.c6
-rw-r--r--libnm-core/tests/test-general.c357
-rw-r--r--libnm/NetworkManager.h7
-rwxr-xr-xlibnm/generate-setting-docs.py3
-rw-r--r--libnm/libnm.ver24
-rw-r--r--libnm/nm-client.c9
-rw-r--r--libnm/nm-device-ovs-bridge.c96
-rw-r--r--libnm/nm-device-ovs-bridge.h45
-rw-r--r--libnm/nm-device-ovs-interface.c96
-rw-r--r--libnm/nm-device-ovs-interface.h45
-rw-r--r--libnm/nm-device-ovs-port.c95
-rw-r--r--libnm/nm-device-ovs-port.h45
-rw-r--r--libnm/nm-device.c9
-rw-r--r--libnm/nm-types.h63
-rw-r--r--man/nm-openvswitch.xml204
-rw-r--r--po/POTFILES.in7
-rw-r--r--shared/nm-meta-setting.c24
-rw-r--r--shared/nm-meta-setting.h4
-rw-r--r--shared/nm-utils/nm-test-utils.h57
-rw-r--r--src/NetworkManagerUtils.c32
-rw-r--r--src/devices/nm-device-logging.h9
-rw-r--r--src/devices/nm-device-ppp.c4
-rw-r--r--src/devices/nm-device.c400
-rw-r--r--src/devices/nm-device.h3
-rw-r--r--src/devices/ovs/nm-device-ovs-bridge.c156
-rw-r--r--src/devices/ovs/nm-device-ovs-bridge.h35
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.c191
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.h35
-rw-r--r--src/devices/ovs/nm-device-ovs-port.c201
-rw-r--r--src/devices/ovs/nm-device-ovs-port.h35
-rw-r--r--src/devices/ovs/nm-ovs-factory.c195
-rw-r--r--src/devices/ovs/nm-ovsdb.c1591
-rw-r--r--src/devices/ovs/nm-ovsdb.h50
-rw-r--r--src/nm-manager.c38
-rw-r--r--src/nm-manager.h7
64 files changed, 6236 insertions, 415 deletions
diff --git a/Makefile.am b/Makefile.am
index 83fe9de83d..46799cb1f1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -206,6 +206,12 @@ introspection_sources = \
introspection/org.freedesktop.NetworkManager.Device.Modem.h \
introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.c \
introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.h \
+ introspection/org.freedesktop.NetworkManager.Device.OvsInterface.c \
+ introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h \
+ introspection/org.freedesktop.NetworkManager.Device.OvsPort.c \
+ introspection/org.freedesktop.NetworkManager.Device.OvsPort.h \
+ introspection/org.freedesktop.NetworkManager.Device.OvsBridge.c \
+ introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h \
introspection/org.freedesktop.NetworkManager.Device.Ppp.c \
introspection/org.freedesktop.NetworkManager.Device.Ppp.h \
introspection/org.freedesktop.NetworkManager.Device.Statistics.c \
@@ -281,6 +287,9 @@ DBUS_INTERFACE_DOCS = \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Tun.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Bridge.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.OlpcMesh.xml \
+ docs/api/dbus-org.freedesktop.NetworkManager.Device.OvsInterface.xml \
+ docs/api/dbus-org.freedesktop.NetworkManager.Device.OvsPort.xml \
+ docs/api/dbus-org.freedesktop.NetworkManager.Device.OvsBridge.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Ppp.xml \
docs/api/dbus-org.freedesktop.NetworkManager.DHCP4Config.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Generic.xml \
@@ -329,6 +338,9 @@ dbusinterfaces_DATA = \
introspection/org.freedesktop.NetworkManager.Device.Macvlan.xml \
introspection/org.freedesktop.NetworkManager.Device.Modem.xml \
introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.xml \
+ introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml \
+ introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml \
+ introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml \
introspection/org.freedesktop.NetworkManager.Device.Ppp.xml \
introspection/org.freedesktop.NetworkManager.Device.Statistics.xml \
introspection/org.freedesktop.NetworkManager.Device.Team.xml \
@@ -393,6 +405,10 @@ 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-ovs-bridge.h \
+ libnm-core/nm-setting-ovs-interface.h \
+ libnm-core/nm-setting-ovs-patch.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 \
@@ -474,6 +490,10 @@ 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-ovs-bridge.c \
+ libnm-core/nm-setting-ovs-interface.c \
+ libnm-core/nm-setting-ovs-patch.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 \
@@ -731,6 +751,9 @@ libnm_lib_h_pub_real = \
libnm/nm-device-macvlan.h \
libnm/nm-device-modem.h \
libnm/nm-device-olpc-mesh.h \
+ libnm/nm-device-ovs-interface.h \
+ libnm/nm-device-ovs-port.h \
+ libnm/nm-device-ovs-bridge.h \
libnm/nm-device-ppp.h \
libnm/nm-device-team.h \
libnm/nm-device-tun.h \
@@ -784,6 +807,9 @@ libnm_lib_c_real = \
libnm/nm-device-macvlan.c \
libnm/nm-device-modem.c \
libnm/nm-device-olpc-mesh.c \
+ libnm/nm-device-ovs-interface.c \
+ libnm/nm-device-ovs-port.c \
+ libnm/nm-device-ovs-bridge.c \
libnm/nm-device-ppp.c \
libnm/nm-device-team.c \
libnm/nm-device-tun.c \
@@ -2743,6 +2769,63 @@ check_local += check-local-devices-team
endif
###############################################################################
+# src/devices/ovs
+###############################################################################
+
+if WITH_OPENVSWITCH
+
+if HAVE_SYSTEMD
+
+systemdnmunitdir = $(systemdsystemunitdir)/NetworkManager.service.d
+systemdnmunit_DATA = \
+ data/NetworkManager-ovs.conf
+
+endif
+
+core_plugins += src/devices/ovs/libnm-device-plugin-ovs.la
+
+src_devices_ovs_libnm_device_plugin_ovs_la_SOURCES = \
+ src/devices/ovs/nm-ovsdb.c \
+ src/devices/ovs/nm-ovsdb.h \
+ src/devices/ovs/nm-ovs-factory.c \
+ src/devices/ovs/nm-device-ovs-interface.c \
+ src/devices/ovs/nm-device-ovs-interface.h \
+ src/devices/ovs/nm-device-ovs-port.c \
+ src/devices/ovs/nm-device-ovs-port.h \
+ src/devices/ovs/nm-device-ovs-bridge.c \
+ src/devices/ovs/nm-device-ovs-bridge.h
+
+src_devices_ovs_libnm_device_plugin_ovs_la_CPPFLAGS = \
+ -I$(srcdir)/src \
+ -I$(builddir)/src \
+ -I$(srcdir)/shared \
+ -I$(builddir)/shared \
+ -I$(builddir)/libnm-core \
+ -I$(srcdir)/libnm-core \
+ \
+ -DG_LOG_DOMAIN=\""NetworkManager"\" \
+ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
+ -DRUNSTATEDIR=\"$(runstatedir)\" \
+ \
+ $(JANSSON_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+src_devices_ovs_libnm_device_plugin_ovs_la_LDFLAGS = \
+ -module -avoid-version \
+ -Wl,--version-script="$(srcdir)/linker-script-devices.ver"
+
+src_devices_ovs_libnm_device_plugin_ovs_la_LIBADD = \
+ introspection/libnmdbus.la \
+ $(JANSSON_LIBS) \
+ $(GLIB_LIBS)
+
+check-local-devices-ovs: src/devices/ovs/libnm-device-plugin-ovs.la
+ $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so "$(srcdir)/linker-script-devices.ver"
+ $(call check_so_symbols,$(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so)
+
+endif
+
+###############################################################################
# src/dnsmasq/tests
###############################################################################
@@ -4292,6 +4375,13 @@ man_pages_autogen += \
man/nm-settings-keyfile.5 \
man/nm-settings.5
+if WITH_OPENVSWITCH
+man_pages += man/nm-openvswitch.7
+else
+EXTRA_DIST += man/nm-openvswitch.7
+dist_dependencies += man/nm-openvswitch.7
+endif
+
if CONFIG_PLUGIN_IFCFG_RH
man_pages_autogen += man/nm-settings-ifcfg-rh.5
else
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index a13af882e2..73c1de2644 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -136,6 +136,10 @@ const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[] = {
NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
NM_SETTING_TEAM_SETTING_NAME","\
NM_SETTING_TEAM_PORT_SETTING_NAME"," \
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME","\
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME","\
+ NM_SETTING_OVS_PATCH_SETTING_NAME","\
+ NM_SETTING_OVS_PORT_SETTING_NAME","\
NM_SETTING_DCB_SETTING_NAME"," \
NM_SETTING_TUN_SETTING_NAME"," \
NM_SETTING_IP_TUNNEL_SETTING_NAME"," \
diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c
index 9423202ca7..bf4dcc77f3 100644
--- a/clients/common/nm-client-utils.c
+++ b/clients/common/nm-client-utils.c
@@ -243,9 +243,11 @@ nmc_device_state_to_string (NMDeviceState state)
return _("deactivating");
case NM_DEVICE_STATE_FAILED:
return _("connection failed");
- default:
+ case NM_DEVICE_STATE_UNKNOWN:
return _("unknown");
}
+
+ return _("unknown");
}
const char *
@@ -260,9 +262,11 @@ nmc_device_metered_to_string (NMMetered value)
return _("yes (guessed)");
case NM_METERED_GUESS_NO:
return _("no (guessed)");
- default:
+ case NM_METERED_UNKNOWN:
return _("unknown");
}
+
+ return _("unknown");
}
const char *
@@ -395,10 +399,13 @@ nmc_device_reason_to_string (NMDeviceStateReason reason)
return _("The device's parent changed");
case NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED:
return _("The device parent's management changed");
- default:
- /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */
- return _("Unknown");
+
+ case NM_DEVICE_STATE_REASON_OVSDB_FAILED:
+ return _("OpenVSwitch database connection failed");
}
+
+ /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */
+ return _("Unknown");
}
const char *
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index 4e90c42643..0231fe06ce 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -5009,6 +5009,8 @@ 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_OVS_BRIDGE_SETTING_NAME,
+ NM_SETTING_OVS_PORT_SETTING_NAME,
NM_SETTING_TEAM_SETTING_NAME),
),
),
@@ -5804,6 +5806,81 @@ static const NMMetaPropertyInfo *const property_infos_PPPOE[] = {
};
#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_BRIDGE
+static const NMMetaPropertyInfo *const property_infos_OVS_BRIDGE[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_FAIL_MODE,
+ .property_type = &_pt_gobject_string,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ .values_static = VALUES_STATIC ("secure", "standalone"),
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE,
+ .property_type = &_pt_gobject_bool,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE,
+ .property_type = &_pt_gobject_bool,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_STP_ENABLE,
+ .property_type = &_pt_gobject_bool,
+ ),
+ NULL
+};
+
+#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_INTERFACE
+static const NMMetaPropertyInfo *const property_infos_OVS_INTERFACE[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_INTERFACE_TYPE,
+ .property_type = &_pt_gobject_string,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ .values_static = VALUES_STATIC ("internal", "patch"),
+ ),
+ ),
+ NULL
+};
+
+#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_PATCH
+static const NMMetaPropertyInfo *const property_infos_OVS_PATCH[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PATCH_PEER,
+ .property_type = &_pt_gobject_string,
+ ),
+ NULL
+};
+
+#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_PORT
+static const NMMetaPropertyInfo *const property_infos_OVS_PORT[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_VLAN_MODE,
+ .property_type = &_pt_gobject_string,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ .values_static = VALUES_STATIC ("access", "native-tagged", "native-untagged", "trunk"),
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_TAG,
+ .property_type = &_pt_gobject_int,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_LACP,
+ .property_type = &_pt_gobject_string,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ .values_static = VALUES_STATIC ("active", "off", "passive"),
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_MODE,
+ .property_type = &_pt_gobject_string,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ .values_static = VALUES_STATIC ("active-backup", "balance-slb", "balance-tcp"),
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_UPDELAY,
+ .property_type = &_pt_gobject_int,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_DOWNDELAY,
+ .property_type = &_pt_gobject_int,
+ ),
+ NULL
+};
+
+#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PPP
static const NMMetaPropertyInfo *const property_infos_PPP[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_NOAUTH,
@@ -6722,6 +6799,10 @@ _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_OVS_BRIDGE N_("OpenVSwitch bridge settings")
+#define SETTING_PRETTY_NAME_OVS_INTERFACE N_("OpenVSwitch interface settings")
+#define SETTING_PRETTY_NAME_OVS_PATCH N_("OpenVSwitch patch interface 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")
@@ -6871,6 +6952,29 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
),
.setting_init_fcn = _setting_init_fcn_olpc_mesh,
),
+ SETTING_INFO (OVS_BRIDGE,
+ .valid_parts = NM_META_SETTING_VALID_PARTS (
+ NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (OVS_BRIDGE, TRUE),
+ ),
+ ),
+ SETTING_INFO (OVS_INTERFACE,
+ .valid_parts = NM_META_SETTING_VALID_PARTS (
+ NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (OVS_INTERFACE, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (OVS_PATCH, FALSE),
+ NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG, FALSE),
+ NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG, FALSE),
+ NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE),
+ ),
+ ),
+ SETTING_INFO (OVS_PATCH),
+ SETTING_INFO (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),
+ ),
+ ),
SETTING_INFO (PPPOE,
/* PPPoE is a base connection type from historical reasons.
* See libnm-core/nm-setting.c:_nm_setting_is_base_type()
@@ -6977,6 +7081,16 @@ static const NMMetaSettingValidPartItem *const valid_settings_slave_bridge[] = {
NULL,
};
+static const NMMetaSettingValidPartItem *const valid_settings_slave_ovs_bridge[] = {
+ NM_META_SETTING_VALID_PART_ITEM (OVS_PORT, FALSE),
+ NULL,
+};
+
+static const NMMetaSettingValidPartItem *const valid_settings_slave_ovs_port[] = {
+ NM_META_SETTING_VALID_PART_ITEM (OVS_INTERFACE, FALSE),
+ NULL,
+};
+
static const NMMetaSettingValidPartItem *const valid_settings_slave_team[] = {
NM_META_SETTING_VALID_PART_ITEM (TEAM_PORT, TRUE),
NULL,
@@ -6997,6 +7111,14 @@ 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_SET_OUT (out_slave_name, "ovs-slave");
+ return valid_settings_slave_ovs_bridge;
+ }
+ if (nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) {
+ NM_SET_OUT (out_slave_name, "ovs-slave");
+ return valid_settings_slave_ovs_port;
+ }
if (nm_streq (slave_type, NM_SETTING_TEAM_SETTING_NAME)) {
NM_SET_OUT (out_slave_name, "team-slave");
return valid_settings_slave_team;
diff --git a/clients/common/settings-docs.c.in b/clients/common/settings-docs.c.in
index 4d1e94e817..1378f2dced 100644
--- a/clients/common/settings-docs.c.in
+++ b/clients/common/settings-docs.c.in
@@ -197,7 +197,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_INPUT_KEY N_("The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_LOCAL N_("The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MODE N_("The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2).")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MTU N_("None")
+#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_OUTPUT_KEY N_("The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_PARENT N_("If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface.")
@@ -262,6 +262,22 @@
#define DESCRIBE_DOC_NM_SETTING_MACVLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
#define DESCRIBE_DOC_NM_SETTING_MACVLAN_PROMISCUOUS N_("Whether the interface should be put in promiscuous mode.")
#define DESCRIBE_DOC_NM_SETTING_MACVLAN_TAP N_("Whether the interface should be a MACVTAP.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_FAIL_MODE N_("The bridge failure mode. One of \"secure\", \"standalone\" or empty.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE N_("Enable or disable multicast snooping.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
+#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_RSTP_ENABLE N_("Enable or disable RSTP.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
+#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_TYPE N_("The interface type. Either \"internal\", or empty.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("Specifies the unicast destination IP address of a remote OpenVSwitch bridge port to connect to.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_MODE N_("Bonding mode. One of \"active-backup\", \"balance-slb\", or \"balance-tcp\".")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_UPDELAY N_("The time port must be active befor it starts forwarding traffic.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_LACP N_("LACP mode. One of \"active\", \"off\", or \"passive\".")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TAG N_("The VLAN tag in the range 0-4095.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\" or unset.")
#define DESCRIBE_DOC_NM_SETTING_PPP_BAUD N_("If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed.")
#define DESCRIBE_DOC_NM_SETTING_PPP_CRTSCTS N_("If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE.")
#define DESCRIBE_DOC_NM_SETTING_PPP_LCP_ECHO_FAILURE N_("If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The \"lcp-echo-interval\" property must also be set to a non-zero value if this property is used.")
diff --git a/configure.ac b/configure.ac
index a4353760a4..146f7ccd15 100644
--- a/configure.ac
+++ b/configure.ac
@@ -871,6 +871,20 @@ if test "$with_dhcpcanon" != "no"; then
else
AC_DEFINE(WITH_DHCPCANON, 0, [Define if you have dhcpcanon])
fi
+
+# OpenVSwitch integration
+AC_ARG_ENABLE(ovs, AS_HELP_STRING([--enable-ovs], [enable OpenVSwitch support]))
+if test "${enable_ovs}" != "no"; then
+ enable_ovs='yes'
+ if test "$have_jansson" = "no"; then
+ AC_MSG_ERROR(Jansson is required for ovs support)
+ fi
+ AC_DEFINE(WITH_OPENVSWITCH, 1, [Define if you have OpenVSwitch support])
+else
+ AC_DEFINE(WITH_OPENVSWITCH, 0, [Define if you have OpenVSwitch support])
+fi
+AM_CONDITIONAL(WITH_OPENVSWITCH, test "${enable_ovs}" = "yes")
+
# DHCP client support
AC_ARG_WITH([dhclient],
AS_HELP_STRING([--with-dhclient=yes|no|path], [Enable dhclient 4.x support]))
@@ -1282,6 +1296,8 @@ if test "$build_docs" != "yes" -a \
-f "$srcdir"/man/nmcli.1 -a \
-f "$srcdir"/man/nmtui.1 -a \
\
+ -f "$srcdir"/man/nm-openvswitch.7 -a \
+ \
-f "$srcdir"/man/nm-settings-ifcfg-rh.5 -a \
-f "$srcdir"/man/nm-settings-keyfile.5 -a \
-f "$srcdir"/man/nm-settings.5 -a \
@@ -1359,6 +1375,7 @@ echo " modemmanager-1: $with_modem_manager_1"
echo " ofono: $with_ofono"
echo " concheck: $enable_concheck"
echo " libteamdctl: $enable_teamdctl"
+echo " ovs: $enable_ovs"
echo " libnm-glib: $with_libnm_glib"
echo " nmcli: $build_nmcli"
echo " nmtui: $build_nmtui"
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 64393526c4..4778f15042 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -50,6 +50,7 @@
%bcond_without wwan
%bcond_without team
%bcond_without wifi
+%bcond_without ovs
%bcond_without ppp
%bcond_without nmtui
%bcond_without regen_docs
@@ -240,6 +241,19 @@ This package contains NetworkManager support for mobile broadband (WWAN)
devices.
%endif
+
+%if %{with ovs}
+%package ovs
+Summary: OpenVSwitch device plugin for NetworkManager
+Group: System Environment/Base
+Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release}
+Requires: openvswitch
+
+%description ovs
+This package contains NetworkManager support for OpenVSwitch bridges.
+%endif
+
+
%if %{with ppp}
%package ppp
Summary: PPP plugin for NetworkManager
@@ -563,7 +577,7 @@ fi
%dir %{nmlibdir}/VPN
%{_mandir}/man1/*
%{_mandir}/man5/*
-%{_mandir}/man7/*
+%{_mandir}/man7/nmcli-examples.7*
%{_mandir}/man8/*
%dir %{_localstatedir}/lib/NetworkManager
%dir %{_sysconfdir}/NetworkManager/system-connections
@@ -608,6 +622,13 @@ fi
%{_libdir}/%{name}/libnm-wwan.so
%endif
+%if %{with ovs}
+%files ovs
+%{_libdir}/%{name}/libnm-device-plugin-ovs.so
+%{systemd_dir}/NetworkManager.service.d/NetworkManager-ovs.conf
+%{_mandir}/man7/nm-openvswitch.7*
+%endif
+
%if %{with ppp}
%files ppp
%{_libdir}/pppd/%{ppp_version}/nm-pppd-plugin.so
diff --git a/data/NetworkManager-ovs.conf b/data/NetworkManager-ovs.conf
new file mode 100644
index 0000000000..dde5ba5897
--- /dev/null
+++ b/data/NetworkManager-ovs.conf
@@ -0,0 +1,2 @@
+[Unit]
+After=openvswitch.service
diff --git a/docs/api/Makefile.am b/docs/api/Makefile.am
index f5def7b8b8..67f0a22691 100644
--- a/docs/api/Makefile.am
+++ b/docs/api/Makefile.am
@@ -88,6 +88,7 @@ content_files = \
$(top_builddir)/man/nmcli-examples.xml \
$(top_builddir)/man/nm-settings.xml \
$(top_builddir)/man/nm-settings-keyfile.xml \
+ $(top_builddir)/man/nm-openvswitch.xml \
version.xml \
../../COPYING \
$(NULL)
diff --git a/docs/api/network-manager-docs.xml b/docs/api/network-manager-docs.xml
index dd5f0e47f5..0002f8ee11 100644
--- a/docs/api/network-manager-docs.xml
+++ b/docs/api/network-manager-docs.xml
@@ -76,6 +76,7 @@
<xi:include href="../../man/nm-settings-keyfile.xml"><xi:fallback /></xi:include>
<xi:include href="../../man/nm-settings-ifcfg-rh.xml"><xi:fallback /></xi:include>
<xi:include href="../../man/nm-online.xml"/>
+ <xi:include href="../../man/nm-openvswitch.xml"/>
</part>
<part id="ref-settings">
diff --git a/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml b/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml
new file mode 100644
index 0000000000..08ed5dc01b
--- /dev/null
+++ b/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <!--
+ org.freedesktop.NetworkManager.Device.OvsBridge:
+ @short_description: OvsBridge Device
+
+ -->
+ <interface name="org.freedesktop.NetworkManager.Device.OvsBridge">
+
+ <!--
+ PropertiesChanged:
+ @properties: A dictionary mapping property names to variant boxed values
+
+ DEPRECATED. Use the standard "PropertiesChanged" signal from "org.freedesktop.DBus.Properties" instead which exists since version NetworkManager 1.2.0.
+ -->
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a{sv}"/>
+ </signal>
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml b/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml
new file mode 100644
index 0000000000..9605a958b4
--- /dev/null
+++ b/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <!--
+ org.freedesktop.NetworkManager.Device.OvsInterface:
+ @short_description: OvsInterface Device
+
+ -->
+ <interface name="org.freedesktop.NetworkManager.Device.OvsInterface">
+
+ <!--
+ PropertiesChanged:
+ @properties: A dictionary mapping property names to variant boxed values
+
+ DEPRECATED. Use the standard "PropertiesChanged" signal from "org.freedesktop.DBus.Properties" instead which exists since version NetworkManager 1.2.0.
+ -->
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a{sv}"/>
+ </signal>
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml b/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml
new file mode 100644
index 0000000000..d4900f510c
--- /dev/null
+++ b/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <!--
+ org.freedesktop.NetworkManager.Device.OvsPort:
+ @short_description: OvsPort Device
+
+ -->
+ <interface name="org.freedesktop.NetworkManager.Device.OvsPort">
+
+ <!--
+ PropertiesChanged:
+ @properties: A dictionary mapping property names to variant boxed values
+
+ DEPRECATED. Use the standard "PropertiesChanged" signal from "org.freedesktop.DBus.Properties" instead which exists since version NetworkManager 1.2.0.
+ -->
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a{sv}"/>
+ </signal>
+ </interface>
+</node>
diff --git a/libnm-core/nm-connection-private.h b/libnm-core/nm-connection-private.h
index 7dd088ace0..ee2d7264b9 100644
--- a/libnm-core/nm-connection-private.h
+++ b/libnm-core/nm-connection-private.h
@@ -34,4 +34,10 @@ const char *_nm_connection_detect_bluetooth_type (NMConnection *self);
gboolean _nm_connection_verify_required_interface_name (NMConnection *connection,
GError **error);
+int _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
+ NMConnection *connection,
+ gboolean normalize,
+ gboolean *out_modified,
+ GError **error);
+
#endif /* __NM_CONNECTION_PRIVATE_H__ */
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 0296c6f9b2..d5e28f7b3e 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -729,6 +729,10 @@ _nm_connection_detect_slave_type (NMConnection *connection, NMSetting **out_s_po
i_slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
else if (!strcmp (name, NM_SETTING_TEAM_PORT_SETTING_NAME))
i_slave_type = NM_SETTING_TEAM_SETTING_NAME;
+ else if (!strcmp (name, NM_SETTING_OVS_PORT_SETTING_NAME))
+ i_slave_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
+ else if (!strcmp (name, NM_SETTING_OVS_INTERFACE_SETTING_NAME))
+ i_slave_type = NM_SETTING_OVS_PORT_SETTING_NAME;
else
continue;
@@ -809,9 +813,20 @@ _normalize_ethernet_link_neg (NMConnection *self)
}
static gboolean
+_without_ip_config (NMConnection *self)
+{
+ const char *connection_type = nm_connection_get_connection_type (self);
+
+ g_return_val_if_fail (connection_type, FALSE);
+ if (strcmp (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0)
+ return FALSE;
+
+ return !!nm_setting_connection_get_master (nm_connection_get_setting_connection (self));
+}
+
+static gboolean
_normalize_ip_config (NMConnection *self, GHashTable *parameters)
{
- NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
const char *default_ip6_method = NULL;
NMSettingIPConfig *s_ip4, *s_ip6;
@@ -829,7 +844,7 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
s_ip6 = nm_connection_get_setting_ip6_config (self);
s_proxy = nm_connection_get_setting_proxy (self);
- if (nm_setting_connection_get_master (s_con)) {
+ if (_without_ip_config (self)) {
/* Slave connections don't have IP configuration. */
if (s_ip4)
@@ -1096,6 +1111,27 @@ _normalize_bluetooth_type (NMConnection *self, GHashTable *parameters)
}
static gboolean
+_normalize_ovs_interface_type (NMConnection *self, GHashTable *parameters)
+{
+ NMSettingOvsInterface *s_ovs_interface = nm_connection_get_setting_ovs_interface (self);
+ gboolean modified;
+ int v;
+
+ if (!s_ovs_interface)
+ return FALSE;
+
+ v = _nm_setting_ovs_interface_verify_interface_type (s_ovs_interface,
+ self,
+ TRUE,
+ &modified,
+ NULL);
+ if (v != TRUE)
+ g_return_val_if_reached (modified);
+
+ return modified;
+}
+
+static gboolean
_normalize_required_settings (NMConnection *self, GHashTable *parameters)
{
NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
@@ -1252,40 +1288,42 @@ _nm_connection_verify (NMConnection *connection, GError **error)
s_ip6 = nm_connection_get_setting_ip6_config (connection);
s_proxy = nm_connection_get_setting_proxy (connection);
- if (nm_setting_connection_get_master (s_con)) {
- if ( NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS,
- NM_SETTING_VERIFY_NORMALIZABLE)
- && (s_ip4 || s_ip6 || s_proxy)) {
- g_clear_error (&normalizable_error);
- g_set_error_literal (&normalizable_error,
- NM_CONNECTION_ERROR,
- NM_CONNECTION_ERROR_INVALID_SETTING,
- _("setting not allowed in slave connection"));
- g_prefix_error (&normalizable_error, "%s: ",
- s_ip4
- ? NM_SETTING_IP4_CONFIG_SETTING_NAME
- : (s_ip6
- ? NM_SETTING_IP6_CONFIG_SETTING_NAME
- : NM_SETTING_PROXY_SETTING_NAME));
- /* having a slave with IP config *was* and is a verify() error. */
- normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
- }
- } else {
- if ( NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS)
- && (!s_ip4 || !s_ip6 || !s_proxy)) {
- g_set_error_literal (&normalizable_error,
- NM_CONNECTION_ERROR,
- NM_CONNECTION_ERROR_MISSING_SETTING,
- _("setting is required for non-slave connections"));
- g_prefix_error (&normalizable_error, "%s: ",
- !s_ip4
- ? NM_SETTING_IP4_CONFIG_SETTING_NAME
- : (!s_ip6
- ? NM_SETTING_IP6_CONFIG_SETTING_NAME
- : NM_SETTING_PROXY_SETTING_NAME));
- /* having a master without IP config was not a verify() error, accept
- * it for backward compatibility. */
- normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
+ nm_assert (normalizable_error_type != NM_SETTING_VERIFY_ERROR);
+ if (NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS,
+ NM_SETTING_VERIFY_NORMALIZABLE)) {
+ if (_without_ip_config (connection)) {
+ if (s_ip4 || s_ip6 || s_proxy) {
+ g_clear_error (&normalizable_error);
+ g_set_error_literal (&normalizable_error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_SETTING,
+ _("setting not allowed in slave connection"));
+ g_prefix_error (&normalizable_error, "%s: ",
+ s_ip4
+ ? NM_SETTING_IP4_CONFIG_SETTING_NAME
+ : (s_ip6
+ ? NM_SETTING_IP6_CONFIG_SETTING_NAME
+ : NM_SETTING_PROXY_SETTING_NAME));
+ /* having a slave with IP config *was* and is a verify() error. */
+ normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+ }
+ } else {
+ if ( normalizable_error_type == NM_SETTING_VERIFY_SUCCESS
+ && (!s_ip4 || !s_ip6 || !s_proxy)) {
+ g_set_error_literal (&normalizable_error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_SETTING,
+ _("setting is required for non-slave connections"));
+ g_prefix_error (&normalizable_error, "%s: ",
+ !s_ip4
+ ? NM_SETTING_IP4_CONFIG_SETTING_NAME
+ : (!s_ip6
+ ? NM_SETTING_IP6_CONFIG_SETTING_NAME
+ : NM_SETTING_PROXY_SETTING_NAME));
+ /* having a master without IP config was not a verify() error, accept
+ * it for backward compatibility. */
+ normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
+ }
}
}
@@ -1397,6 +1435,7 @@ nm_connection_normalize (NMConnection *connection,
was_modified |= _normalize_team_config (connection, parameters);
was_modified |= _normalize_team_port_config (connection, parameters);
was_modified |= _normalize_bluetooth_type (connection, parameters);
+ was_modified |= _normalize_ovs_interface_type (connection, parameters);
/* Verify anew. */
success = _nm_connection_verify (connection, error);
@@ -1414,6 +1453,7 @@ nm_connection_normalize (NMConnection *connection,
NM_CONNECTION_ERROR_FAILED,
_("Unexpected failure to normalize the connection"));
}
+ g_warning ("connection did not verify after normalization: %s", error ? (*error)->message : "??");
g_return_val_if_reached (FALSE);
}
@@ -1747,52 +1787,82 @@ _for_each_sort (NMSetting **p_a, NMSetting **p_b, void *unused)
}
/**
- * nm_connection_for_each_setting_value:
- * @connection: the #NMConnection
- * @func: (scope call): user-supplied function called for each setting's property
- * @user_data: user data passed to @func at each invocation
+ * nm_connection_get_settings:
+ * @connection: the #NMConnection instance
+ * @out_length: (allow-none): (out): the length of the returned array
*
- * Iterates over the properties of each #NMSetting object in the #NMConnection,
- * calling the supplied user function for each property.
- **/
-void
-nm_connection_for_each_setting_value (NMConnection *connection,
- NMSettingValueIterFn func,
- gpointer user_data)
+ * Retrieves the settings in @connection.
+ *
+ * The returned array is %NULL-terminated.
+ *
+ * Returns: (array length=out_length) (transfer container): a
+ * %NULL-terminated array containing every setting of
+ * @connection.
+ * If the connection has no settings, %NULL is returned.
+ *
+ * Since: 1.10
+ */
+NMSetting **
+nm_connection_get_settings (NMConnection *connection,
+ guint *out_length)
{
NMConnectionPrivate *priv;
- gs_free NMSetting **arr_free = NULL;
- NMSetting *arr_temp[20], **arr;
+ NMSetting **arr;
GHashTableIter iter;
- gpointer value;
+ NMSetting *setting;
guint i, size;
- g_return_if_fail (NM_IS_CONNECTION (connection));
- g_return_if_fail (func);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
priv = NM_CONNECTION_GET_PRIVATE (connection);
size = g_hash_table_size (priv->settings);
- if (!size)
- return;
- if (size > G_N_ELEMENTS (arr_temp))
- arr = arr_free = g_new (NMSetting *, size);
- else
- arr = arr_temp;
+ if (!size) {
+ NM_SET_OUT (out_length, 0);
+ return NULL;
+ }
+
+ arr = g_new (NMSetting *, size + 1);
g_hash_table_iter_init (&iter, priv->settings);
- for (i = 0; g_hash_table_iter_next (&iter, NULL, &value); i++)
- arr[i] = NM_SETTING (value);
- g_assert (i == size);
+ for (i = 0; g_hash_table_iter_next (&iter, NULL, (gpointer *) &setting); i++)
+ arr[i] = setting;
+ nm_assert (i == size);
+ arr[size] = NULL;
/* sort the settings. This has an effect on the order in which keyfile
* prints them. */
if (size > 1)
g_qsort_with_data (arr, size, sizeof (NMSetting *), (GCompareDataFunc) _for_each_sort, NULL);
- for (i = 0; i < size; i++)
- nm_setting_enumerate_values (arr[i], func, user_data);
+ NM_SET_OUT (out_length, size);
+ return arr;
+}
+
+/**
+ * nm_connection_for_each_setting_value:
+ * @connection: the #NMConnection
+ * @func: (scope call): user-supplied function called for each setting's property
+ * @user_data: user data passed to @func at each invocation
+ *
+ * Iterates over the properties of each #NMSetting object in the #NMConnection,
+ * calling the supplied user function for each property.
+ **/
+void
+nm_connection_for_each_setting_value (NMConnection *connection,
+ NMSettingValueIterFn func,
+ gpointer user_data)
+{
+ gs_free NMSetting **settings = NULL;
+ guint i, length = 0;
+
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (func);
+
+ settings = nm_connection_get_settings (connection, &length);
+ for (i = 0; i < length; i++)
+ nm_setting_enumerate_values (settings[i], func, user_data);
}
/**
@@ -1983,6 +2053,9 @@ 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_OVS_BRIDGE_SETTING_NAME)
+ || !strcmp (type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)
+ || !strcmp (type, NM_SETTING_OVS_PORT_SETTING_NAME)
|| !strcmp (type, NM_SETTING_VXLAN_SETTING_NAME))
return TRUE;
@@ -2336,6 +2409,70 @@ nm_connection_get_setting_olpc_mesh (NMConnection *connection)
}
/**
+ * nm_connection_get_setting_ovs_bridge:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsBridge the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsBridge if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsBridge *
+nm_connection_get_setting_ovs_bridge (NMConnection *connection)
+{
+ return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_BRIDGE);
+}
+
+/**
+ * nm_connection_get_setting_ovs_interface:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsInterface the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsInterface if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsInterface *
+nm_connection_get_setting_ovs_interface (NMConnection *connection)
+{
+ return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_INTERFACE);
+}
+
+/**
+ * nm_connection_get_setting_ovs_patch:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsPatch the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsPatch if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsPatch *
+nm_connection_get_setting_ovs_patch (NMConnection *connection)
+{
+ return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PATCH);
+}
+
+/**
+ * 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);
+}
+
+/**
* nm_connection_get_setting_ppp:
* @connection: the #NMConnection
*
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index fae9862a40..c6b23a00bc 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -179,6 +179,10 @@ void nm_connection_for_each_setting_value (NMConnection *connection,
NMSettingValueIterFn func,
gpointer user_data);
+NM_AVAILABLE_IN_1_10
+NMSetting ** nm_connection_get_settings (NMConnection *connection,
+ guint *out_length);
+
void nm_connection_dump (NMConnection *connection);
/* Helpers */
@@ -213,6 +217,13 @@ NMSettingMacsec * nm_connection_get_setting_macsec (NMConnec
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
+NMSettingOvsBridge * nm_connection_get_setting_ovs_bridge (NMConnection *connection);
+NM_AVAILABLE_IN_1_10
+NMSettingOvsInterface * nm_connection_get_setting_ovs_interface (NMConnection *connection);
+NMSettingOvsPatch * nm_connection_get_setting_ovs_patch (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 adba8ed116..59ebfcb132 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -56,6 +56,10 @@
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-patch.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 3b9ec37b8e..ae680b7a5a 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -50,6 +50,10 @@ typedef struct _NMSettingIP6Config NMSettingIP6Config;
typedef struct _NMSettingMacsec NMSettingMacsec;
typedef struct _NMSettingMacvlan NMSettingMacvlan;
typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh;
+typedef struct _NMSettingOvsBridge NMSettingOvsBridge;
+typedef struct _NMSettingOvsInterface NMSettingOvsInterface;
+typedef struct _NMSettingOvsPatch NMSettingOvsPatch;
+typedef struct _NMSettingOvsPort NMSettingOvsPort;
typedef struct _NMSettingPpp NMSettingPpp;
typedef struct _NMSettingPppoe NMSettingPppoe;
typedef struct _NMSettingSerial NMSettingSerial;
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 8b92dbe7e6..7f75c84955 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -36,43 +36,46 @@
/*
* dbus services details
*/
-#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
-
-#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
-#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
-#define NM_DBUS_INTERFACE_DEVICE NM_DBUS_INTERFACE ".Device"
-#define NM_DBUS_INTERFACE_DEVICE_WIRED NM_DBUS_INTERFACE_DEVICE ".Wired"
-#define NM_DBUS_INTERFACE_DEVICE_ADSL NM_DBUS_INTERFACE_DEVICE ".Adsl"
-#define NM_DBUS_INTERFACE_DEVICE_WIRELESS NM_DBUS_INTERFACE_DEVICE ".Wireless"
-#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH NM_DBUS_INTERFACE_DEVICE ".Bluetooth"
-#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
-#define NM_DBUS_PATH_ACCESS_POINT NM_DBUS_PATH "/AccessPoint"
-#define NM_DBUS_INTERFACE_ACCESS_POINT NM_DBUS_INTERFACE ".AccessPoint"
-#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem"
-#define NM_DBUS_INTERFACE_DEVICE_WIMAX NM_DBUS_INTERFACE_DEVICE ".WiMax"
-#define NM_DBUS_INTERFACE_WIMAX_NSP NM_DBUS_INTERFACE ".WiMax.Nsp"
-#define NM_DBUS_PATH_WIMAX_NSP NM_DBUS_PATH "/Nsp"
-#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active"
-#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config"
-#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config"
-#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config"
-#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config"
-#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
-#define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond"
-#define NM_DBUS_INTERFACE_DEVICE_DUMMY NM_DBUS_INTERFACE_DEVICE ".Dummy"
-#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team"
-#define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan"
-#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge"
-#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic"
-#define NM_DBUS_INTERFACE_DEVICE_VETH NM_DBUS_INTERFACE_DEVICE ".Veth"
-#define NM_DBUS_INTERFACE_DEVICE_TUN NM_DBUS_INTERFACE_DEVICE ".Tun"
-#define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec"
-#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan"
-#define NM_DBUS_INTERFACE_DEVICE_PPP NM_DBUS_INTERFACE_DEVICE ".Ppp"
-#define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan"
-#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
-#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
-#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics"
+#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
+
+#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
+#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
+#define NM_DBUS_INTERFACE_DEVICE NM_DBUS_INTERFACE ".Device"
+#define NM_DBUS_INTERFACE_DEVICE_WIRED NM_DBUS_INTERFACE_DEVICE ".Wired"
+#define NM_DBUS_INTERFACE_DEVICE_ADSL NM_DBUS_INTERFACE_DEVICE ".Adsl"
+#define NM_DBUS_INTERFACE_DEVICE_WIRELESS NM_DBUS_INTERFACE_DEVICE ".Wireless"
+#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH NM_DBUS_INTERFACE_DEVICE ".Bluetooth"
+#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE NM_DBUS_INTERFACE_DEVICE ".OvsInterface"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_PORT NM_DBUS_INTERFACE_DEVICE ".OvsPort"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE NM_DBUS_INTERFACE_DEVICE ".OvsBridge"
+#define NM_DBUS_PATH_ACCESS_POINT NM_DBUS_PATH "/AccessPoint"
+#define NM_DBUS_INTERFACE_ACCESS_POINT NM_DBUS_INTERFACE ".AccessPoint"
+#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem"
+#define NM_DBUS_INTERFACE_DEVICE_WIMAX NM_DBUS_INTERFACE_DEVICE ".WiMax"
+#define NM_DBUS_INTERFACE_WIMAX_NSP NM_DBUS_INTERFACE ".WiMax.Nsp"
+#define NM_DBUS_PATH_WIMAX_NSP NM_DBUS_PATH "/Nsp"
+#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active"
+#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config"
+#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config"
+#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config"
+#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config"
+#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
+#define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond"
+#define NM_DBUS_INTERFACE_DEVICE_DUMMY NM_DBUS_INTERFACE_DEVICE ".Dummy"
+#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team"
+#define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan"
+#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge"
+#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic"
+#define NM_DBUS_INTERFACE_DEVICE_VETH NM_DBUS_INTERFACE_DEVICE ".Veth"
+#define NM_DBUS_INTERFACE_DEVICE_TUN NM_DBUS_INTERFACE_DEVICE ".Tun"
+#define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec"
+#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan"
+#define NM_DBUS_INTERFACE_DEVICE_PPP NM_DBUS_INTERFACE_DEVICE ".Ppp"
+#define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan"
+#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
+#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
+#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics"
#define NM_DBUS_INTERFACE_SETTINGS "org.freedesktop.NetworkManager.Settings"
#define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings"
@@ -205,35 +208,41 @@ typedef enum {
* @NM_DEVICE_TYPE_MACSEC: a MACsec interface
* @NM_DEVICE_TYPE_DUMMY: a dummy interface
* @NM_DEVICE_TYPE_PPP: a PPP interface
+ * @NM_DEVICE_TYPE_OVS_INTERFACE: a OpenVSwitch interface
+ * @NM_DEVICE_TYPE_OVS_PORT: a OpenVSwitch port
+ * @NM_DEVICE_TYPE_OVS_BRIDGE: a OpenVSwitch bridge
*
* #NMDeviceType values indicate the type of hardware represented by a
* device object.
**/
typedef enum {
- NM_DEVICE_TYPE_UNKNOWN = 0,
- NM_DEVICE_TYPE_ETHERNET = 1,
- NM_DEVICE_TYPE_WIFI = 2,
- NM_DEVICE_TYPE_UNUSED1 = 3,
- NM_DEVICE_TYPE_UNUSED2 = 4,
- NM_DEVICE_TYPE_BT = 5, /* Bluetooth */
- NM_DEVICE_TYPE_OLPC_MESH = 6,
- NM_DEVICE_TYPE_WIMAX = 7,
- NM_DEVICE_TYPE_MODEM = 8,
- NM_DEVICE_TYPE_INFINIBAND = 9,
- NM_DEVICE_TYPE_BOND = 10,
- NM_DEVICE_TYPE_VLAN = 11,
- NM_DEVICE_TYPE_ADSL = 12,
- NM_DEVICE_TYPE_BRIDGE = 13,
- NM_DEVICE_TYPE_GENERIC = 14,
- NM_DEVICE_TYPE_TEAM = 15,
- NM_DEVICE_TYPE_TUN = 16,
- NM_DEVICE_TYPE_IP_TUNNEL = 17,
- NM_DEVICE_TYPE_MACVLAN = 18,
- NM_DEVICE_TYPE_VXLAN = 19,
- NM_DEVICE_TYPE_VETH = 20,
- NM_DEVICE_TYPE_MACSEC = 21,
- NM_DEVICE_TYPE_DUMMY = 22,
- NM_DEVICE_TYPE_PPP = 23,
+ NM_DEVICE_TYPE_UNKNOWN = 0,
+ NM_DEVICE_TYPE_ETHERNET = 1,
+ NM_DEVICE_TYPE_WIFI = 2,
+ NM_DEVICE_TYPE_UNUSED1 = 3,
+ NM_DEVICE_TYPE_UNUSED2 = 4,
+ NM_DEVICE_TYPE_BT = 5, /* Bluetooth */
+ NM_DEVICE_TYPE_OLPC_MESH = 6,
+ NM_DEVICE_TYPE_WIMAX = 7,
+ NM_DEVICE_TYPE_MODEM = 8,
+ NM_DEVICE_TYPE_INFINIBAND = 9,
+ NM_DEVICE_TYPE_BOND = 10,
+ NM_DEVICE_TYPE_VLAN = 11,
+ NM_DEVICE_TYPE_ADSL = 12,
+ NM_DEVICE_TYPE_BRIDGE = 13,
+ NM_DEVICE_TYPE_GENERIC = 14,
+ NM_DEVICE_TYPE_TEAM = 15,
+ NM_DEVICE_TYPE_TUN = 16,
+ NM_DEVICE_TYPE_IP_TUNNEL = 17,
+ NM_DEVICE_TYPE_MACVLAN = 18,
+ NM_DEVICE_TYPE_VXLAN = 19,
+ NM_DEVICE_TYPE_VETH = 20,
+ NM_DEVICE_TYPE_MACSEC = 21,
+ NM_DEVICE_TYPE_DUMMY = 22,
+ NM_DEVICE_TYPE_PPP = 23,
+ NM_DEVICE_TYPE_OVS_INTERFACE = 24,
+ NM_DEVICE_TYPE_OVS_PORT = 25,
+ NM_DEVICE_TYPE_OVS_BRIDGE = 26,
} NMDeviceType;
/**
@@ -541,6 +550,7 @@ typedef enum {
* @NM_DEVICE_STATE_REASON_NEW_ACTIVATION: New connection activation was enqueued
* @NM_DEVICE_STATE_REASON_PARENT_CHANGED: the device's parent changed
* @NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED: the device parent's management changed
+ * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with OpenVSwitch database
*
* Device state change reason codes
*/
@@ -608,6 +618,7 @@ typedef enum {
NM_DEVICE_STATE_REASON_NEW_ACTIVATION = 60,
NM_DEVICE_STATE_REASON_PARENT_CHANGED = 61,
NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED = 62,
+ NM_DEVICE_STATE_REASON_OVSDB_FAILED = 63,
} NMDeviceStateReason;
/**
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index 728367c9b2..28d7a8f6b7 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -859,6 +859,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
gboolean is_slave;
const char *slave_setting_type;
NMSetting *normerr_base_type = NULL;
+ const char *type;
+ const char *slave_type;
const char *normerr_slave_setting_type = NULL;
const char *normerr_missing_slave_type = NULL;
const char *normerr_missing_slave_type_port = NULL;
@@ -904,8 +906,10 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
}
}
- if (!priv->type) {
- if (!connection || !(normerr_base_type = _nm_connection_find_base_type_setting (connection))) {
+ type = priv->type;
+ if (!type) {
+ if ( !connection
+ || !(normerr_base_type = _nm_connection_find_base_type_setting (connection))) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_MISSING_PROPERTY,
@@ -913,10 +917,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
return FALSE;
}
+ type = nm_setting_get_name (normerr_base_type);
} else {
GType base_type;
- if (!priv->type[0]) {
+ if (!type[0]) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -925,21 +930,21 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
- base_type = nm_setting_lookup_type (priv->type);
+ base_type = nm_setting_lookup_type (type);
if ( base_type == G_TYPE_INVALID
|| _nm_setting_type_get_base_type_priority (base_type) == NM_SETTING_PRIORITY_INVALID) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("connection type '%s' is not valid"),
- priv->type);
+ type);
g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
return FALSE;
}
/* Make sure the corresponding 'type' item is present */
if ( connection
- && !nm_connection_get_setting_by_name (connection, priv->type)) {
+ && !nm_connection_get_setting_by_name (connection, type)) {
NMSetting *s_base;
NMConnection *connection2;
@@ -952,7 +957,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
g_object_unref (connection2);
if (!normerr_base_setting) {
- _set_error_missing_base_setting (error, priv->type);
+ _set_error_missing_base_setting (error, type);
return FALSE;
}
}
@@ -960,13 +965,14 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
is_slave = FALSE;
slave_setting_type = NULL;
- if (priv->slave_type) {
- is_slave = _nm_setting_slave_type_is_valid (priv->slave_type, &slave_setting_type);
+ slave_type = priv->slave_type;
+ if (slave_type) {
+ is_slave = _nm_setting_slave_type_is_valid (slave_type, &slave_setting_type);
if (!is_slave) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
- _("Unknown slave type '%s'"), priv->slave_type);
+ _("Unknown slave type '%s'"), slave_type);
g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
return FALSE;
}
@@ -986,8 +992,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
&& !nm_connection_get_setting_by_name (connection, slave_setting_type))
normerr_slave_setting_type = slave_setting_type;
} else {
+ nm_assert (!slave_type);
if (priv->master) {
- const char *slave_type;
NMSetting *s_port;
if ( connection
@@ -1006,6 +1012,18 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
}
}
+ if ( nm_streq0 (type, NM_SETTING_OVS_PORT_SETTING_NAME)
+ && !nm_streq0 (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("Only '%s' connections can be enslaved to '%s'"),
+ NM_SETTING_OVS_PORT_SETTING_NAME,
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+ return FALSE;
+ }
+
if (priv->metered != NM_METERED_UNKNOWN &&
priv->metered != NM_METERED_YES &&
priv->metered != NM_METERED_NO) {
diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c
index 7e1737fc3b..fa8d4a7a24 100644
--- a/libnm-core/nm-setting-ip-tunnel.c
+++ b/libnm-core/nm-setting-ip-tunnel.c
@@ -758,7 +758,7 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *setting_class)
G_PARAM_STATIC_STRINGS));
/**
- * NMSettingIPTunel:mtu:
+ * NMSettingIPTunnel:mtu:
*
* If non-zero, only transmit packets of the specified size or smaller,
* breaking larger packets up into multiple fragments.
diff --git a/libnm-core/nm-setting-ovs-bridge.c b/libnm-core/nm-setting-ovs-bridge.c
new file mode 100644
index 0000000000..fbae95d0c9
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-bridge.c
@@ -0,0 +1,337 @@
+/* -*- 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-bridge.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-bridge
+ * @short_description: Describes connection properties for OpenVSwitch bridges.
+ *
+ * The #NMSettingOvsBridge object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch bridges.
+ **/
+
+enum {
+ PROP_0,
+ PROP_FAIL_MODE,
+ PROP_MCAST_SNOOPING_ENABLE,
+ PROP_RSTP_ENABLE,
+ PROP_STP_ENABLE,
+ LAST_PROP
+};
+
+/**
+ * NMSettingOvsBridge:
+ *
+ * OvsBridge Link Settings
+ */
+struct _NMSettingOvsBridge {
+ NMSetting parent;
+
+ char *fail_mode;
+ gboolean mcast_snooping_enable;
+ gboolean rstp_enable;
+ gboolean stp_enable;
+};
+
+struct _NMSettingOvsBridgeClass {
+ NMSettingClass parent;
+};
+
+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_ovs_bridge_get_fail_mode:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:fail_mode property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_bridge_get_fail_mode (NMSettingOvsBridge *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), NULL);
+
+ return self->fail_mode;
+}
+
+/**
+ * nm_setting_ovs_bridge_get_mcast_snooping_enable:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:mcast_snooping_enable property of the setting
+ *
+ * Since: 1.10
+ **/
+gboolean
+nm_setting_ovs_bridge_get_mcast_snooping_enable (NMSettingOvsBridge *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE);
+
+ return self->mcast_snooping_enable;
+}
+
+/**
+ * nm_setting_ovs_bridge_get_rstp_enable:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:rstp_enable property of the setting
+ *
+ * Since: 1.10
+ **/
+gboolean
+nm_setting_ovs_bridge_get_rstp_enable (NMSettingOvsBridge *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE);
+
+ return self->rstp_enable;
+}
+
+/**
+ * nm_setting_ovs_bridge_get_stp_enable:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:stp_enable property of the setting
+ *
+ * Since: 1.10
+ **/
+gboolean
+nm_setting_ovs_bridge_get_stp_enable (NMSettingOvsBridge *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE);
+
+ return self->stp_enable;
+}
+
+/*****************************************************************************/
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (setting);
+
+ if (!_nm_connection_verify_required_interface_name (connection, error))
+ return FALSE;
+
+ if (connection) {
+ NMSettingConnection *s_con;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ if (!s_con) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_SETTING,
+ _("missing setting"));
+ g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+ return FALSE;
+ }
+
+ if (nm_setting_connection_get_master (s_con)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with a '%s' setting must not have a master."),
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+ return FALSE;
+ }
+ }
+
+ if (!NM_IN_STRSET (self->fail_mode, "secure", "standalone", NULL)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not allowed in fail_mode"),
+ self->fail_mode);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_BRIDGE_SETTING_NAME, NM_SETTING_OVS_BRIDGE_FAIL_MODE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object);
+
+ switch (prop_id) {
+ case PROP_FAIL_MODE:
+ g_value_set_string (value, self->fail_mode);
+ break;
+ case PROP_MCAST_SNOOPING_ENABLE:
+ g_value_set_boolean (value, self->mcast_snooping_enable);
+ break;
+ case PROP_RSTP_ENABLE:
+ g_value_set_boolean (value, self->rstp_enable);
+ break;
+ case PROP_STP_ENABLE:
+ g_value_set_boolean (value, self->stp_enable);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object);
+
+ switch (prop_id) {
+ case PROP_FAIL_MODE:
+ g_free (self->fail_mode);
+ self->fail_mode = g_value_dup_string (value);
+ break;
+ case PROP_MCAST_SNOOPING_ENABLE:
+ self->mcast_snooping_enable = g_value_get_boolean (value);
+ break;
+ case PROP_RSTP_ENABLE:
+ self->rstp_enable = g_value_get_boolean (value);
+ break;
+ case PROP_STP_ENABLE:
+ self->stp_enable = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_bridge_init (NMSettingOvsBridge *self)
+{
+}
+
+/**
+ * nm_setting_ovs_bridge_new:
+ *
+ * Creates a new #NMSettingOvsBridge object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsBridge object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_bridge_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_BRIDGE, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object);
+
+ g_free (self->fail_mode);
+
+ G_OBJECT_CLASS (nm_setting_ovs_bridge_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /**
+ * NMSettingOvsBridge:fail-mode:
+ *
+ * The bridge failure mode. One of "secure", "standalone" or empty.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FAIL_MODE,
+ g_param_spec_string (NM_SETTING_OVS_BRIDGE_FAIL_MODE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsBridge:mcast-snooping-enable:
+ *
+ * Enable or disable multicast snooping.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MCAST_SNOOPING_ENABLE,
+ g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsBridge:rstp-enable:
+ *
+ * Enable or disable RSTP.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_RSTP_ENABLE,
+ g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsBridge:stp-enable:
+ *
+ * Enable or disable STP.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STP_ENABLE,
+ g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_STP_ENABLE, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-bridge.h b/libnm-core/nm-setting-ovs-bridge.h
new file mode 100644
index 0000000000..d4837e58a2
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-bridge.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_BRIDGE_H__
+#define __NM_SETTING_OVS_BRIDGE_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_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_OVS_BRIDGE_SETTING_NAME "ovs-bridge"
+
+#define NM_SETTING_OVS_BRIDGE_FAIL_MODE "fail-mode"
+#define NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE "mcast-snooping-enable"
+#define NM_SETTING_OVS_BRIDGE_RSTP_ENABLE "rstp-enable"
+#define NM_SETTING_OVS_BRIDGE_STP_ENABLE "stp-enable"
+
+typedef struct _NMSettingOvsBridgeClass NMSettingOvsBridgeClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_bridge_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_bridge_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_bridge_get_fail_mode (NMSettingOvsBridge *self);
+NM_AVAILABLE_IN_1_10
+gboolean nm_setting_ovs_bridge_get_mcast_snooping_enable (NMSettingOvsBridge *self);
+NM_AVAILABLE_IN_1_10
+gboolean nm_setting_ovs_bridge_get_rstp_enable (NMSettingOvsBridge *self);
+NM_AVAILABLE_IN_1_10
+gboolean nm_setting_ovs_bridge_get_stp_enable (NMSettingOvsBridge *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_BRIDGE_H__ */
diff --git a/libnm-core/nm-setting-ovs-interface.c b/libnm-core/nm-setting-ovs-interface.c
new file mode 100644
index 0000000000..f06db32abb
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-interface.c
@@ -0,0 +1,392 @@
+/*
+ * 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-interface.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-interface
+ * @short_description: Describes connection properties for OpenVSwitch interfaces.
+ *
+ * The #NMSettingOvsInterface object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch interfaces.
+ **/
+
+enum {
+ PROP_0,
+ PROP_TYPE,
+ LAST_PROP
+};
+
+/**
+ * NMSettingOvsInterface:
+ *
+ * OpenVSwitch Interface Settings
+ */
+struct _NMSettingOvsInterface {
+ NMSetting parent;
+
+ char *type;
+};
+
+struct _NMSettingOvsInterfaceClass {
+ NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsInterface, nm_setting_ovs_interface, NM_TYPE_SETTING,
+ _nm_register_setting (OVS_INTERFACE, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_INTERFACE)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_interface_get_interface_type:
+ * @self: the #NMSettingOvsInterface
+ *
+ * Returns: the #NMSettingOvsInterface:type property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_interface_get_interface_type (NMSettingOvsInterface *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_INTERFACE (self), NULL);
+
+ return self->type;
+}
+
+/*****************************************************************************/
+
+int
+_nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
+ NMConnection *connection,
+ gboolean normalize,
+ gboolean *out_modified,
+ GError **error)
+{
+ gboolean has_patch;
+ const char *type;
+ const char *connection_type;
+ gboolean is_ovs_connection_type;
+ gboolean missing_patch_setting = FALSE;
+
+ g_return_val_if_fail (NM_IS_SETTING_OVS_INTERFACE (self), FALSE);
+ if (normalize) {
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ nm_assert (self == nm_connection_get_setting_ovs_interface (connection));
+ } else
+ g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE);
+
+ NM_SET_OUT (out_modified, FALSE);
+
+ type = self ? self->type : NULL;
+
+ if ( type
+ && !NM_IN_STRSET (type,
+ "internal",
+ "system",
+ "patch")) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid interface type"),
+ type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ return FALSE;
+ }
+
+ if (!connection)
+ return TRUE;
+
+ connection_type = nm_connection_get_connection_type (connection);
+ if (!connection_type) {
+ /* if we have an ovs-interface, then the connection type must be either
+ * "ovs-interface" (for non "system" type) or anything else (for "system" type).
+ *
+ * The connection type usually can be normalized based on the presence of a
+ * base setting. However, in this case, if the connection type is missing,
+ * that is too complicate to guess what the user wanted.
+ *
+ * Require the use to be explicit and fail. */
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with a '%s' setting needs connection.type explicitly set"),
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+ return FALSE;
+ }
+
+ if (nm_streq (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) {
+ if ( type
+ && nm_streq (type, "system")) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection of type '%s' cannot have ovs-interface.type \"system\""),
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ return FALSE;
+ }
+ is_ovs_connection_type = TRUE;
+ } else {
+ if ( type
+ && !nm_streq (type, "system")) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection of type '%s' cannot have an ovs-interface.type \"%s\""),
+ connection_type,
+ type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ return FALSE;
+ }
+ is_ovs_connection_type = FALSE;
+ }
+
+ has_patch = !!nm_connection_get_setting_by_name (connection, NM_SETTING_OVS_PATCH_SETTING_NAME);
+
+ if (has_patch) {
+ if (!is_ovs_connection_type) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with '%s' setting must be of connection.type \"ovs-interface\" but is \"%s\""),
+ NM_SETTING_OVS_PATCH_SETTING_NAME,
+ connection_type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ return FALSE;
+ }
+ if (type) {
+ if (!nm_streq (type, "patch")) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with '%s' setting needs to be of 'patch' interface type, not '%s'"),
+ NM_SETTING_OVS_PATCH_SETTING_NAME,
+ type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ type = "patch";
+ goto normalize;
+ } else {
+ if (nm_streq0 (type, "patch")) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_SETTING,
+ _("A connection with ovs-interface.type '%s' setting a 'ovs-patch' setting"),
+ type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ return FALSE;
+ }
+ }
+
+ if (type)
+ return TRUE;
+
+ if (is_ovs_connection_type)
+ type = "internal";
+ else
+ type = "system";
+normalize:
+ if (!normalize) {
+ if (!self) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_SETTING,
+ _("Missing ovs interface setting"));
+ g_prefix_error (error, "%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ } else {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("Missing ovs interface type"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+ }
+ if (missing_patch_setting) {
+ }
+ return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+ }
+
+ if (!self) {
+ self = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (connection, NM_SETTING (self));
+ }
+ g_object_set (self,
+ NM_SETTING_OVS_INTERFACE_TYPE, type,
+ NULL);
+ NM_SET_OUT (out_modified, TRUE);
+
+ return TRUE;
+}
+
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (setting);
+
+ if (connection) {
+ NMSettingConnection *s_con;
+ const char *slave_type;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ if (!s_con) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_SETTING,
+ _("missing setting"));
+ g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+ return FALSE;
+ }
+
+ if (!nm_setting_connection_get_master (s_con)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with a '%s' setting must have a master."),
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+ return FALSE;
+ }
+
+ slave_type = nm_setting_connection_get_slave_type (s_con);
+ if ( slave_type
+ && !nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with a '%s' setting must have the slave-type set to '%s'. Instead it is '%s'"),
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+ NM_SETTING_OVS_PORT_SETTING_NAME,
+ slave_type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
+ return FALSE;
+ }
+ }
+
+ return _nm_setting_ovs_interface_verify_interface_type (self,
+ connection,
+ FALSE,
+ NULL,
+ error);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object);
+
+ switch (prop_id) {
+ case PROP_TYPE:
+ g_value_set_string (value, self->type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object);
+
+ switch (prop_id) {
+ case PROP_TYPE:
+ g_free (self->type);
+ self->type = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_interface_init (NMSettingOvsInterface *self)
+{
+}
+
+/**
+ * nm_setting_ovs_interface_new:
+ *
+ * Creates a new #NMSettingOvsInterface object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsInterface object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_interface_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_INTERFACE, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object);
+
+ g_free (self->type);
+
+ G_OBJECT_CLASS (nm_setting_ovs_interface_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /**
+ * NMSettingOvsInterface:type:
+ *
+ * The interface type. Either "internal", or empty.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TYPE,
+ g_param_spec_string (NM_SETTING_OVS_INTERFACE_TYPE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-interface.h b/libnm-core/nm-setting-ovs-interface.h
new file mode 100644
index 0000000000..7261cfd8c9
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-interface.h
@@ -0,0 +1,54 @@
+/*
+ * 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_INTERFACE_H__
+#define __NM_SETTING_OVS_INTERFACE_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_INTERFACE (nm_setting_ovs_interface_get_type ())
+#define NM_SETTING_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_INTERFACE, NMSettingOvsInterface))
+#define NM_SETTING_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_INTERFACECONFIG, NMSettingOvsInterfaceClass))
+#define NM_IS_SETTING_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_INTERFACE))
+#define NM_IS_SETTING_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_INTERFACE))
+#define NM_SETTING_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_INTERFACE, NMSettingOvsInterfaceClass))
+
+#define NM_SETTING_OVS_INTERFACE_SETTING_NAME "ovs-interface"
+
+#define NM_SETTING_OVS_INTERFACE_TYPE "type"
+
+typedef struct _NMSettingOvsInterfaceClass NMSettingOvsInterfaceClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_interface_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_interface_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_interface_get_interface_type (NMSettingOvsInterface *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_INTERFACE_H__ */
diff --git a/libnm-core/nm-setting-ovs-patch.c b/libnm-core/nm-setting-ovs-patch.c
new file mode 100644
index 0000000000..9e38091474
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-patch.c
@@ -0,0 +1,215 @@
+/* -*- 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-patch.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-patch
+ * @short_description: Describes connection properties for OpenVSwitch patch interfaces.
+ *
+ * The #NMSettingOvsPatch object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch interfaces of type "patch".
+ **/
+
+enum {
+ PROP_0,
+ PROP_PEER,
+ LAST_PROP
+};
+
+/**
+ * NMSettingOvsPatch:
+ *
+ * OvsPatch Link Settings
+ */
+struct _NMSettingOvsPatch {
+ NMSetting parent;
+
+ char *peer;
+};
+
+struct _NMSettingOvsPatchClass {
+ NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsPatch, nm_setting_ovs_patch, NM_TYPE_SETTING,
+ _nm_register_setting (OVS_PATCH, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_PATCH)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_patch_get_peer:
+ * @self: the #NMSettingOvsPatch
+ *
+ * Returns: the #NMSettingOvsPatch:peer property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_patch_get_peer (NMSettingOvsPatch *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PATCH (self), NULL);
+
+ return self->peer;
+}
+
+/*****************************************************************************/
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (setting);
+ int family = AF_UNSPEC;
+
+ if (!_nm_connection_verify_required_interface_name (connection, error))
+ return FALSE;
+
+ if (!self->peer) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ",
+ NM_SETTING_OVS_PATCH_SETTING_NAME,
+ NM_SETTING_OVS_PATCH_PEER);
+ return FALSE;
+ }
+
+ if (nm_utils_ipaddr_valid (AF_INET, self->peer))
+ family = AF_INET;
+ else if (nm_utils_ipaddr_valid (AF_INET6, self->peer))
+ family = AF_INET6;
+ else {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid IP address"),
+ self->peer);
+ g_prefix_error (error, "%s.%s: ",
+ NM_SETTING_OVS_PATCH_SETTING_NAME,
+ NM_SETTING_OVS_PATCH_PEER);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object);
+
+ switch (prop_id) {
+ case PROP_PEER:
+ g_value_set_string (value, self->peer);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object);
+
+ switch (prop_id) {
+ case PROP_PEER:
+ g_free (self->peer);
+ self->peer = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_patch_init (NMSettingOvsPatch *self)
+{
+}
+
+/**
+ * nm_setting_ovs_patch_new:
+ *
+ * Creates a new #NMSettingOvsPatch object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsPatch object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_patch_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_PATCH, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object);
+
+ g_free (self->peer);
+
+ G_OBJECT_CLASS (nm_setting_ovs_patch_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /**
+ * NMSettingOvsPatch:peer:
+ *
+ * Specifies the unicast destination IP address of a remote OpenVSwitch
+ * bridge port to connect to.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PEER,
+ g_param_spec_string (NM_SETTING_OVS_PATCH_PEER, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-patch.h b/libnm-core/nm-setting-ovs-patch.h
new file mode 100644
index 0000000000..091f0d43d5
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-patch.h
@@ -0,0 +1,54 @@
+/*
+ * 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_PATCH_H__
+#define __NM_SETTING_OVS_PATCH_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_PATCH (nm_setting_ovs_patch_get_type ())
+#define NM_SETTING_OVS_PATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_PATCH, NMSettingOvsPatch))
+#define NM_SETTING_OVS_PATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_PATCHCONFIG, NMSettingOvsPatchClass))
+#define NM_IS_SETTING_OVS_PATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_PATCH))
+#define NM_IS_SETTING_OVS_PATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_PATCH))
+#define NM_SETTING_OVS_PATCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_PATCH, NMSettingOvsPatchClass))
+
+#define NM_SETTING_OVS_PATCH_SETTING_NAME "ovs-patch"
+
+#define NM_SETTING_OVS_PATCH_PEER "peer"
+
+typedef struct _NMSettingOvsPatchClass NMSettingOvsPatchClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_patch_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_patch_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_patch_get_peer (NMSettingOvsPatch *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_PATCH_H__ */
diff --git a/libnm-core/nm-setting-ovs-port.c b/libnm-core/nm-setting-ovs-port.c
new file mode 100644
index 0000000000..bd05f2e7b2
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-port.c
@@ -0,0 +1,471 @@
+/* -*- 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.
+ **/
+
+enum {
+ PROP_0,
+ PROP_VLAN_MODE,
+ PROP_TAG,
+ PROP_LACP,
+ PROP_BOND_MODE,
+ PROP_BOND_UPDELAY,
+ PROP_BOND_DOWNDELAY,
+ LAST_PROP
+};
+
+/**
+ * NMSettingOvsPort:
+ *
+ * OvsPort Link Settings
+ */
+struct _NMSettingOvsPort {
+ NMSetting parent;
+
+ char *vlan_mode;
+ guint tag;
+ char *lacp;
+ char *bond_mode;
+ guint bond_updelay;
+ guint bond_downdelay;
+};
+
+struct _NMSettingOvsPortClass {
+ NMSettingClass parent;
+};
+
+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_get_vlan_mode:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:vlan-mode property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_port_get_vlan_mode (NMSettingOvsPort *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL);
+
+ return self->vlan_mode;
+}
+
+/**
+ * nm_setting_ovs_port_get_tag:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:tag property of the setting
+ *
+ * Since: 1.10
+ **/
+guint
+nm_setting_ovs_port_get_tag (NMSettingOvsPort *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0);
+
+ return self->tag;
+}
+
+/**
+ * nm_setting_ovs_port_get_lacp:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:lacp property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_port_get_lacp (NMSettingOvsPort *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL);
+
+ return self->lacp;
+}
+
+/**
+ * nm_setting_ovs_port_get_bond_mode:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:bond-mode property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_port_get_bond_mode (NMSettingOvsPort *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL);
+
+ return self->bond_mode;
+}
+
+/**
+ * nm_setting_ovs_port_get_bond_updelay:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:bond-updelay property of the setting
+ *
+ * Since: 1.10
+ **/
+guint
+nm_setting_ovs_port_get_bond_updelay (NMSettingOvsPort *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0);
+
+ return self->bond_updelay;
+}
+
+/**
+ * nm_setting_ovs_port_get_bond_downdelay:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:bond-downdelay property of the setting
+ *
+ * Since: 1.10
+ **/
+guint
+nm_setting_ovs_port_get_bond_downdelay (NMSettingOvsPort *self)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0);
+
+ return self->bond_downdelay;
+}
+
+/*****************************************************************************/
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingOvsPort *self = NM_SETTING_OVS_PORT (setting);
+
+ if (!_nm_connection_verify_required_interface_name (connection, error))
+ return FALSE;
+
+ if (connection) {
+ NMSettingConnection *s_con;
+ const char *slave_type;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ if (!s_con) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_SETTING,
+ _("missing setting"));
+ g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+ return FALSE;
+ }
+
+ if (!nm_setting_connection_get_master (s_con)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with a '%s' setting must have a master."),
+ NM_SETTING_OVS_PORT_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+ return FALSE;
+ }
+
+ slave_type = nm_setting_connection_get_slave_type (s_con);
+ if ( slave_type
+ && strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("A connection with a '%s' setting must have the slave-type set to '%s'. Instead it is '%s'"),
+ NM_SETTING_OVS_PORT_SETTING_NAME,
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+ slave_type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
+ return FALSE;
+ }
+ }
+
+ if (!NM_IN_STRSET (self->vlan_mode, "access", "native-tagged", "native-untagged", "trunk", NULL)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not allowed in vlan_mode"),
+ self->vlan_mode);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_VLAN_MODE);
+ return FALSE;
+ }
+
+ if (self->tag >= 4095) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("the tag id must be in range 0-4094 but is %u"),
+ self->tag);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_TAG);
+ return FALSE;
+ }
+
+ if (!NM_IN_STRSET (self->lacp, "active", "off", "passive", NULL)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not allowed in lacp"),
+ self->lacp);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_LACP);
+ return FALSE;
+ }
+
+ if (!NM_IN_STRSET (self->bond_mode, "active-backup", "balance-slb", "balance-tcp", NULL)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not allowed in bond_mode"),
+ self->bond_mode);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_BOND_MODE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object);
+
+ switch (prop_id) {
+ case PROP_VLAN_MODE:
+ g_value_set_string (value, self->vlan_mode);
+ break;
+ case PROP_TAG:
+ g_value_set_uint (value, self->tag);
+ break;
+ case PROP_LACP:
+ g_value_set_string (value, self->lacp);
+ break;
+ case PROP_BOND_MODE:
+ g_value_set_string (value, self->bond_mode);
+ break;
+ case PROP_BOND_UPDELAY:
+ g_value_set_uint (value, self->bond_updelay);
+ break;
+ case PROP_BOND_DOWNDELAY:
+ g_value_set_uint (value, self->bond_downdelay);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object);
+
+ switch (prop_id) {
+ case PROP_VLAN_MODE:
+ g_free (self->vlan_mode);
+ self->vlan_mode = g_value_dup_string (value);
+ break;
+ case PROP_TAG:
+ self->tag = g_value_get_uint (value);
+ break;
+ case PROP_LACP:
+ g_free (self->lacp);
+ self->lacp = g_value_dup_string (value);
+ break;
+ case PROP_BOND_MODE:
+ g_free (self->bond_mode);
+ self->bond_mode = g_value_dup_string (value);
+ break;
+ case PROP_BOND_UPDELAY:
+ self->bond_updelay = g_value_get_uint (value);
+ break;
+ case PROP_BOND_DOWNDELAY:
+ self->bond_downdelay = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_port_init (NMSettingOvsPort *self)
+{
+}
+
+/**
+ * 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 void
+finalize (GObject *object)
+{
+ NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object);
+
+ g_free (self->vlan_mode);
+ g_free (self->lacp);
+ g_free (self->bond_mode);
+
+ G_OBJECT_CLASS (nm_setting_ovs_port_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_port_class_init (NMSettingOvsPortClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /**
+ * NMSettingOvsPort:vlan-mode:
+ *
+ * The VLAN mode. One of "access", "native-tagged", "native-untagged",
+ * "trunk" or unset.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VLAN_MODE,
+ g_param_spec_string (NM_SETTING_OVS_PORT_VLAN_MODE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsPort:tag:
+ *
+ * The VLAN tag in the range 0-4095.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TAG,
+ g_param_spec_uint (NM_SETTING_OVS_PORT_TAG, "", "",
+ 0, 4095, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsPort:lacp:
+ *
+ * LACP mode. One of "active", "off", or "passive".
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_LACP,
+ g_param_spec_string (NM_SETTING_OVS_PORT_LACP, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsPort:bond-mode:
+ *
+ * Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp".
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BOND_MODE,
+ g_param_spec_string (NM_SETTING_OVS_PORT_BOND_MODE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+
+ /**
+ * NMSettingOvsPort:bond-updelay:
+ *
+ * The time port must be active befor it starts forwarding traffic.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BOND_UPDELAY,
+ g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_UPDELAY, "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOvsPort:bond-downdelay:
+ *
+ * The time port must be inactive in order to be considered down.
+ *
+ * Since: 1.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BOND_DOWNDELAY,
+ g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_DOWNDELAY, "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-port.h b/libnm-core/nm-setting-ovs-port.h
new file mode 100644
index 0000000000..4c14f123df
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-port.h
@@ -0,0 +1,69 @@
+/*
+ * 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"
+
+#define NM_SETTING_OVS_PORT_VLAN_MODE "vlan-mode"
+#define NM_SETTING_OVS_PORT_TAG "tag"
+#define NM_SETTING_OVS_PORT_LACP "lacp"
+#define NM_SETTING_OVS_PORT_BOND_MODE "bond-mode"
+#define NM_SETTING_OVS_PORT_BOND_UPDELAY "bond-updelay"
+#define NM_SETTING_OVS_PORT_BOND_DOWNDELAY "bond-downdelay"
+
+typedef struct _NMSettingOvsPortClass 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);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_port_get_vlan_mode (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+guint nm_setting_ovs_port_get_tag (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_port_get_lacp (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_port_get_bond_mode (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+guint nm_setting_ovs_port_get_bond_updelay (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+guint nm_setting_ovs_port_get_bond_downdelay (NMSettingOvsPort *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_PORT_H__ */
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index 2f282db6aa..9c8e53aeac 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -219,7 +219,7 @@ _nm_setting_get_base_type_priority (NMSetting *setting)
GType
nm_setting_lookup_type (const char *name)
{
- SettingInfo *info;
+ const SettingInfo *info;
g_return_val_if_fail (name, G_TYPE_INVALID);
@@ -258,6 +258,10 @@ _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))
+ port_type = NM_SETTING_OVS_PORT_SETTING_NAME;
+ else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
+ port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
port_type = NM_SETTING_TEAM_PORT_SETTING_NAME;
else
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 5f4540c57a..0dc7616d6c 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -4655,6 +4655,344 @@ test_connection_normalize_shared_addresses (void)
}
static void
+test_connection_normalize_ovs_interface_type_system (gconstpointer test_data)
+{
+ const guint TEST_CASE = GPOINTER_TO_UINT (test_data);
+ gs_unref_object NMConnection *con = NULL;
+ NMSettingConnection *s_con;
+ NMSettingOvsInterface *s_ovs_if;
+
+ con = nmtst_create_minimal_connection ("test_connection_normalize_ovs_interface_type_system",
+ NULL,
+ NM_SETTING_WIRED_SETTING_NAME, &s_con);
+
+ switch (TEST_CASE) {
+ case 1:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+
+ nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING);
+
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ s_ovs_if = nm_connection_get_setting_ovs_interface (con);
+ g_assert (s_ovs_if);
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "system");
+ break;
+ case 2:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+
+ s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+ nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "system");
+ break;
+ case 3:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+
+ s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "system",
+ NULL);
+ nmtst_assert_connection_verifies_without_normalization (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ break;
+ case 4:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+
+ s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+ NULL);
+ /* the setting doesn't verify, because the interface-type must be "system". */
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ break;
+ case 5:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NULL);
+
+ s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "system",
+ NULL);
+ nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_assert (s_con == nm_connection_get_setting_connection (con));
+ g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+ break;
+ case 6:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
+ NULL);
+
+ s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "system",
+ NULL);
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ break;
+ case 7:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
+ NULL);
+
+ nm_connection_add_setting (con, nm_setting_bridge_port_new ());
+
+ s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "system",
+ NULL);
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+test_connection_normalize_ovs_interface_type_ovs_interface (gconstpointer test_data)
+{
+ const guint TEST_CASE = GPOINTER_TO_UINT (test_data);
+ gs_unref_object NMConnection *con = NULL;
+ NMSettingConnection *s_con;
+ NMSettingOvsInterface *s_ovs_if;
+ NMSettingOvsPatch *s_ovs_patch;
+ NMSettingIP4Config *s_ip4;
+ NMSettingIP6Config *s_ip6;
+
+ con = nmtst_create_minimal_connection ("test_connection_normalize_ovs_interface_type_ovs_interface",
+ NULL,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME, &s_con);
+ s_ovs_if = nm_connection_get_setting_ovs_interface (con);
+ g_assert (s_ovs_if);
+
+ switch (TEST_CASE) {
+ case 1:
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ break;
+ case 2:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NULL);
+ nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_PROXY_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_assert (s_con == nm_connection_get_setting_connection (con));
+ g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+ g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+ break;
+ case 3:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_PROXY_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_assert (s_con == nm_connection_get_setting_connection (con));
+ g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+ g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+ break;
+ case 4:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+ NULL);
+ nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_PROXY_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_assert (s_con == nm_connection_get_setting_connection (con));
+ g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+ g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+ break;
+ case 5:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+ NULL);
+ nm_connection_add_setting (con, nm_setting_ip4_config_new ());
+ nm_connection_add_setting (con, nm_setting_ip6_config_new ());
+ nm_connection_add_setting (con, nm_setting_proxy_new ());
+ s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting_ip4_config (con));
+ s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (con));
+ g_object_set (s_ip4,
+ NM_SETTING_IP_CONFIG_METHOD, "auto",
+ NULL);
+ g_object_set (s_ip6,
+ NM_SETTING_IP_CONFIG_METHOD, "auto",
+ NULL);
+ nmtst_assert_connection_verifies_without_normalization (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_PROXY_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ break;
+ case 6:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+ NULL);
+ nmtst_assert_connection_verifies_and_normalizable (con);
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_PROXY_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+ g_assert (s_con == nm_connection_get_setting_connection (con));
+ g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+ g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+ break;
+ case 7:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "system",
+ NULL);
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ break;
+ case 8:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "bogus",
+ NULL);
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ break;
+ case 9:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+ NULL);
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING);
+ break;
+ case 10:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+ NULL);
+ nm_connection_add_setting (con, nm_setting_ovs_patch_new ());
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+ break;
+ case 11:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NM_SETTING_CONNECTION_INTERFACE_NAME, "adsf",
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+ NULL);
+ nm_connection_add_setting (con, nm_setting_ovs_patch_new ());
+ nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+ break;
+ case 12:
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_MASTER, "master0",
+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+ NM_SETTING_CONNECTION_INTERFACE_NAME, "adsf",
+ NULL);
+ g_object_set (s_ovs_if,
+ NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+ NULL);
+ s_ovs_patch = NM_SETTING_OVS_PATCH (nm_setting_ovs_patch_new ());
+ nm_connection_add_setting (con, NM_SETTING (s_ovs_patch));
+ g_object_set (s_ovs_patch,
+ NM_SETTING_OVS_PATCH_PEER, "1.2.3.4",
+ NULL);
+ nmtst_assert_connection_verifies_and_normalizable (con);
+ nmtst_connection_normalize (con);
+ nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_PROXY_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+ NM_SETTING_OVS_PATCH_SETTING_NAME);
+ g_assert (s_con == nm_connection_get_setting_connection (con));
+ g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+ g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+ g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "patch");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
test_setting_ip4_gateway (void)
{
NMConnection *conn;
@@ -6540,6 +6878,25 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/test_connection_normalize_gateway_never_default", test_connection_normalize_gateway_never_default);
g_test_add_func ("/core/general/test_connection_normalize_may_fail", test_connection_normalize_may_fail);
g_test_add_func ("/core/general/test_connection_normalize_shared_addresses", test_connection_normalize_shared_addresses);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/1", GUINT_TO_POINTER (1), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/2", GUINT_TO_POINTER (2), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/3", GUINT_TO_POINTER (3), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/4", GUINT_TO_POINTER (4), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/5", GUINT_TO_POINTER (5), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/6", GUINT_TO_POINTER (6), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/7", GUINT_TO_POINTER (7), test_connection_normalize_ovs_interface_type_system);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/1", GUINT_TO_POINTER (1), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/2", GUINT_TO_POINTER (2), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/3", GUINT_TO_POINTER (3), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/4", GUINT_TO_POINTER (4), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/5", GUINT_TO_POINTER (5), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/6", GUINT_TO_POINTER (6), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/7", GUINT_TO_POINTER (7), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/8", GUINT_TO_POINTER (8), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/9", GUINT_TO_POINTER (9), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/10", GUINT_TO_POINTER (10), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/11", GUINT_TO_POINTER (11), test_connection_normalize_ovs_interface_type_ovs_interface);
+ g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/12", GUINT_TO_POINTER (12), test_connection_normalize_ovs_interface_type_ovs_interface);
g_test_add_func ("/core/general/test_setting_connection_permissions_helpers", test_setting_connection_permissions_helpers);
g_test_add_func ("/core/general/test_setting_connection_permissions_property", test_setting_connection_permissions_property);
diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h
index 97327f92d6..27737e0e1b 100644
--- a/libnm/NetworkManager.h
+++ b/libnm/NetworkManager.h
@@ -41,6 +41,9 @@
#include "nm-device-macvlan.h"
#include "nm-device-modem.h"
#include "nm-device-olpc-mesh.h"
+#include "nm-device-ovs-interface.h"
+#include "nm-device-ovs-port.h"
+#include "nm-device-ovs-bridge.h"
#include "nm-device-team.h"
#include "nm-device-tun.h"
#include "nm-device-vlan.h"
@@ -73,6 +76,10 @@
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-patch.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/generate-setting-docs.py b/libnm/generate-setting-docs.py
index 9a8df4bfd1..aa0c96ebb2 100755
--- a/libnm/generate-setting-docs.py
+++ b/libnm/generate-setting-docs.py
@@ -257,6 +257,9 @@ for settingxml in settings:
prop_upper = prop.upper().replace('-', '_')
+ if value_desc is None:
+ raise Exception("%s.%s needs a documentation description" % (setting.props.name, prop))
+
if default_value is not None:
outfile.write(" <property name=\"%s\" name_upper=\"%s\" type=\"%s\" default=\"%s\" description=\"%s\" />\n" %
(prop, prop_upper, value_type, escape(default_value), escape(value_desc)))
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 0fd28beecd..1b245b277b 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1185,11 +1185,35 @@ global:
nm_client_connectivity_check_get_available;
nm_client_connectivity_check_get_enabled;
nm_client_connectivity_check_set_enabled;
+ nm_connection_get_settings;
nm_device_dummy_get_hw_address;
+ nm_device_ovs_bridge_get_type;
+ nm_device_ovs_interface_get_type;
+ nm_device_ovs_port_get_type;
nm_device_ppp_get_type;
nm_ip_route_equal_full;
nm_setting_bridge_get_group_forward_mask;
nm_setting_ip_config_get_route_table;
+ nm_setting_ovs_bridge_get_fail_mode;
+ nm_setting_ovs_bridge_get_mcast_snooping_enable;
+ nm_setting_ovs_bridge_get_rstp_enable;
+ nm_setting_ovs_bridge_get_stp_enable;
+ nm_setting_ovs_bridge_get_type;
+ nm_setting_ovs_bridge_new;
+ nm_setting_ovs_interface_get_interface_type;
+ nm_setting_ovs_interface_get_type;
+ nm_setting_ovs_interface_new;
+ nm_setting_ovs_patch_get_peer;
+ nm_setting_ovs_patch_get_type;
+ nm_setting_ovs_patch_new;
+ nm_setting_ovs_port_get_bond_downdelay;
+ nm_setting_ovs_port_get_bond_mode;
+ nm_setting_ovs_port_get_bond_updelay;
+ nm_setting_ovs_port_get_lacp;
+ nm_setting_ovs_port_get_tag;
+ nm_setting_ovs_port_get_type;
+ nm_setting_ovs_port_get_vlan_mode;
+ nm_setting_ovs_port_new;
nm_setting_pppoe_get_parent;
nm_setting_wireless_security_get_pmf;
nm_setting_wireless_security_get_wps_method;
diff --git a/libnm/nm-client.c b/libnm/nm-client.c
index 8912a5f419..8c7e293147 100644
--- a/libnm/nm-client.c
+++ b/libnm/nm-client.c
@@ -63,6 +63,9 @@
#include "nm-device-macvlan.h"
#include "nm-device-modem.h"
#include "nm-device-olpc-mesh.h"
+#include "nm-device-ovs-interface.h"
+#include "nm-device-ovs-port.h"
+#include "nm-device-ovs-bridge.h"
#include "nm-device-ppp.h"
#include "nm-device-team.h"
#include "nm-device-tun.h"
@@ -2147,6 +2150,12 @@ obj_nm_for_gdbus_object (NMClient *self, GDBusObject *object, GDBusObjectManager
type = NM_TYPE_DEVICE_MODEM;
else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OLPC_MESH) == 0)
type = NM_TYPE_DEVICE_OLPC_MESH;
+ else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE) == 0)
+ type = NM_TYPE_DEVICE_OVS_INTERFACE;
+ else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_PORT) == 0)
+ type = NM_TYPE_DEVICE_OVS_PORT;
+ else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE) == 0)
+ type = NM_TYPE_DEVICE_OVS_BRIDGE;
else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_PPP) == 0)
type = NM_TYPE_DEVICE_PPP;
else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_TEAM) == 0)
diff --git a/libnm/nm-device-ovs-bridge.c b/libnm/nm-device-ovs-bridge.c
new file mode 100644
index 0000000000..798f6d20de
--- /dev/null
+++ b/libnm/nm-device-ovs-bridge.c
@@ -0,0 +1,96 @@
+/*
+ * 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 <string.h>
+
+#include "nm-device-ovs-bridge.h"
+#include "nm-object-private.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-port.h"
+#include "nm-setting-connection.h"
+
+/**
+ * NMDeviceOvsBridge:
+ */
+struct _NMDeviceOvsBridge {
+ NMDevice parent;
+};
+
+struct _NMDeviceOvsBridgeClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ return "ovs-bridge";
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ const char *iface_name;
+
+ if (!NM_DEVICE_CLASS (nm_device_ovs_bridge_parent_class)->connection_compatible (device, connection, error))
+ return FALSE;
+
+ if (!nm_connection_is_type (connection, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+ _("The connection was not a ovs_bridge connection."));
+ return FALSE;
+ }
+
+ iface_name = nm_connection_get_interface_name (connection);
+ if (!iface_name) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+ _("The connection did not specify an interface name."));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_OVS_BRIDGE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_ovs_bridge_init (NMDeviceOvsBridge *device)
+{
+}
+
+static void
+nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *ovs_bridge_class)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (ovs_bridge_class);
+
+ device_class->get_type_description = get_type_description;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+}
diff --git a/libnm/nm-device-ovs-bridge.h b/libnm/nm-device-ovs-bridge.h
new file mode 100644
index 0000000000..b4b0b98b00
--- /dev/null
+++ b/libnm/nm-device-ovs-bridge.h
@@ -0,0 +1,45 @@
+/*
+ * 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_DEVICE_OVS_BRIDGE_H__
+#define __NM_DEVICE_OVS_BRIDGE_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_OVS_BRIDGE (nm_device_ovs_bridge_get_type ())
+#define NM_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge))
+#define NM_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass))
+#define NM_IS_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_BRIDGE))
+#define NM_IS_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_BRIDGE))
+#define NM_DEVICE_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass))
+
+typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_device_ovs_bridge_get_type (void);
+
+G_END_DECLS
+
+#endif /* __NM_DEVICE_OVS_BRIDGE_H__ */
diff --git a/libnm/nm-device-ovs-interface.c b/libnm/nm-device-ovs-interface.c
new file mode 100644
index 0000000000..ed3dbd4ca4
--- /dev/null
+++ b/libnm/nm-device-ovs-interface.c
@@ -0,0 +1,96 @@
+/*
+ * 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 <string.h>
+
+#include "nm-device-ovs-interface.h"
+#include "nm-object-private.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-port.h"
+#include "nm-setting-connection.h"
+
+/**
+ * NMDeviceOvsInterface:
+ */
+struct _NMDeviceOvsInterface {
+ NMDevice parent;
+};
+
+struct _NMDeviceOvsInterfaceClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ return "ovs-interface";
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ const char *iface_name;
+
+ if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->connection_compatible (device, connection, error))
+ return FALSE;
+
+ if (!nm_connection_is_type (connection, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+ _("The connection was not a ovs_interface connection."));
+ return FALSE;
+ }
+
+ iface_name = nm_connection_get_interface_name (connection);
+ if (!iface_name) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+ _("The connection did not specify an interface name."));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_OVS_INTERFACE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_ovs_interface_init (NMDeviceOvsInterface *device)
+{
+}
+
+static void
+nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *ovs_interface_class)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (ovs_interface_class);
+
+ device_class->get_type_description = get_type_description;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+}
diff --git a/libnm/nm-device-ovs-interface.h b/libnm/nm-device-ovs-interface.h
new file mode 100644
index 0000000000..a49db559ae
--- /dev/null
+++ b/libnm/nm-device-ovs-interface.h
@@ -0,0 +1,45 @@
+/*
+ * 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_DEVICE_OVS_INTERFACE_H__
+#define __NM_DEVICE_OVS_INTERFACE_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_OVS_INTERFACE (nm_device_ovs_interface_get_type ())
+#define NM_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface))
+#define NM_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass))
+#define NM_IS_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_INTERFACE))
+#define NM_IS_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_INTERFACE))
+#define NM_DEVICE_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass))
+
+typedef struct _NMDeviceOvsInterfaceClass NMDeviceOvsInterfaceClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_device_ovs_interface_get_type (void);
+
+G_END_DECLS
+
+#endif /* __NM_DEVICE_OVS_INTERFACE_H__ */
diff --git a/libnm/nm-device-ovs-port.c b/libnm/nm-device-ovs-port.c
new file mode 100644
index 0000000000..17176dba3c
--- /dev/null
+++ b/libnm/nm-device-ovs-port.c
@@ -0,0 +1,95 @@
+/*
+ * 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 <string.h>
+
+#include "nm-device-ovs-port.h"
+#include "nm-object-private.h"
+#include "nm-setting-ovs-port.h"
+#include "nm-setting-ovs-port.h"
+#include "nm-setting-connection.h"
+
+/**
+ * NMDeviceOvsPort:
+ */
+struct _NMDeviceOvsPort {
+ NMDevice parent;
+};
+
+struct _NMDeviceOvsPortClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ return "ovs-port";
+}
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ const char *iface_name;
+
+ if (!NM_DEVICE_CLASS (nm_device_ovs_port_parent_class)->connection_compatible (device, connection, error))
+ return FALSE;
+
+ if (!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 ovs_port connection."));
+ return FALSE;
+ }
+
+ iface_name = nm_connection_get_interface_name (connection);
+ if (!iface_name) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+ _("The connection did not specify an interface name."));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_OVS_PORT;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_ovs_port_init (NMDeviceOvsPort *device)
+{
+}
+
+static void
+nm_device_ovs_port_class_init (NMDeviceOvsPortClass *ovs_port_class)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (ovs_port_class);
+
+ device_class->get_type_description = get_type_description;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+}
diff --git a/libnm/nm-device-ovs-port.h b/libnm/nm-device-ovs-port.h
new file mode 100644
index 0000000000..af661e7b82
--- /dev/null
+++ b/libnm/nm-device-ovs-port.h
@@ -0,0 +1,45 @@
+/*
+ * 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_DEVICE_OVS_PORT_H__
+#define __NM_DEVICE_OVS_PORT_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_OVS_PORT (nm_device_ovs_port_get_type ())
+#define NM_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort))
+#define NM_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass))
+#define NM_IS_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_PORT))
+#define NM_IS_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_PORT))
+#define NM_DEVICE_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass))
+
+typedef struct _NMDeviceOvsPortClass NMDeviceOvsPortClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_device_ovs_port_get_type (void);
+
+G_END_DECLS
+
+#endif /* __NM_DEVICE_OVS_PORT_H__ */
diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index 7f15436638..65afd078dc 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -261,6 +261,9 @@ coerce_type (NMDeviceType type)
case NM_DEVICE_TYPE_WIFI:
case NM_DEVICE_TYPE_BT:
case NM_DEVICE_TYPE_OLPC_MESH:
+ case NM_DEVICE_TYPE_OVS_INTERFACE:
+ case NM_DEVICE_TYPE_OVS_PORT:
+ case NM_DEVICE_TYPE_OVS_BRIDGE:
case NM_DEVICE_TYPE_WIMAX:
case NM_DEVICE_TYPE_MODEM:
case NM_DEVICE_TYPE_INFINIBAND:
@@ -1522,6 +1525,12 @@ get_type_name (NMDevice *device)
return _("Bluetooth");
case NM_DEVICE_TYPE_OLPC_MESH:
return _("OLPC Mesh");
+ case NM_DEVICE_TYPE_OVS_INTERFACE:
+ return _("OpenVSwitch Interface");
+ case NM_DEVICE_TYPE_OVS_PORT:
+ return _("OpenVSwitch Port");
+ case NM_DEVICE_TYPE_OVS_BRIDGE:
+ return _("OpenVSwitch Bridge");
case NM_DEVICE_TYPE_WIMAX:
return _("WiMAX");
case NM_DEVICE_TYPE_MODEM:
diff --git a/libnm/nm-types.h b/libnm/nm-types.h
index 1e06d15feb..4310120fec 100644
--- a/libnm/nm-types.h
+++ b/libnm/nm-types.h
@@ -26,35 +26,38 @@
#include "nm-dbus-interface.h"
#include "nm-connection.h"
-typedef struct _NMAccessPoint NMAccessPoint;
-typedef struct _NMActiveConnection NMActiveConnection;
-typedef struct _NMClient NMClient;
-typedef struct _NMDevice NMDevice;
-typedef struct _NMDeviceAdsl NMDeviceAdsl;
-typedef struct _NMDeviceBond NMDeviceBond;
-typedef struct _NMDeviceBridge NMDeviceBridge;
-typedef struct _NMDeviceBt NMDeviceBt;
-typedef struct _NMDeviceDummy NMDeviceDummy;
-typedef struct _NMDeviceEthernet NMDeviceEthernet;
-typedef struct _NMDeviceGeneric NMDeviceGeneric;
-typedef struct _NMDeviceInfiniband NMDeviceInfiniband;
-typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel;
-typedef struct _NMDeviceMacsec NMDeviceMacsec;
-typedef struct _NMDeviceMacvlan NMDeviceMacvlan;
-typedef struct _NMDeviceModem NMDeviceModem;
-typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh;
-typedef struct _NMDevicePpp NMDevicePpp;
-typedef struct _NMDeviceTeam NMDeviceTeam;
-typedef struct _NMDeviceTun NMDeviceTun;
-typedef struct _NMDeviceVlan NMDeviceVlan;
-typedef struct _NMDeviceVxlan NMDeviceVxlan;
-typedef struct _NMDeviceWifi NMDeviceWifi;
-typedef struct _NMDeviceWimax NMDeviceWimax;
-typedef struct _NMDhcpConfig NMDhcpConfig;
-typedef struct _NMIPConfig NMIPConfig;
-typedef struct _NMObject NMObject;
-typedef struct _NMRemoteConnection NMRemoteConnection;
-typedef struct _NMVpnConnection NMVpnConnection;
-typedef struct _NMWimaxNsp NMWimaxNsp;
+typedef struct _NMAccessPoint NMAccessPoint;
+typedef struct _NMActiveConnection NMActiveConnection;
+typedef struct _NMClient NMClient;
+typedef struct _NMDevice NMDevice;
+typedef struct _NMDeviceAdsl NMDeviceAdsl;
+typedef struct _NMDeviceBond NMDeviceBond;
+typedef struct _NMDeviceBridge NMDeviceBridge;
+typedef struct _NMDeviceBt NMDeviceBt;
+typedef struct _NMDeviceDummy NMDeviceDummy;
+typedef struct _NMDeviceEthernet NMDeviceEthernet;
+typedef struct _NMDeviceGeneric NMDeviceGeneric;
+typedef struct _NMDeviceInfiniband NMDeviceInfiniband;
+typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel;
+typedef struct _NMDeviceMacsec NMDeviceMacsec;
+typedef struct _NMDeviceMacvlan NMDeviceMacvlan;
+typedef struct _NMDeviceModem NMDeviceModem;
+typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh;
+typedef struct _NMDeviceOvsBridge NMDeviceOvsBridge;
+typedef struct _NMDeviceOvsInterface NMDeviceOvsInterface;
+typedef struct _NMDeviceOvsPort NMDeviceOvsPort;
+typedef struct _NMDevicePpp NMDevicePpp;
+typedef struct _NMDeviceTeam NMDeviceTeam;
+typedef struct _NMDeviceTun NMDeviceTun;
+typedef struct _NMDeviceVlan NMDeviceVlan;
+typedef struct _NMDeviceVxlan NMDeviceVxlan;
+typedef struct _NMDeviceWifi NMDeviceWifi;
+typedef struct _NMDeviceWimax NMDeviceWimax;
+typedef struct _NMDhcpConfig NMDhcpConfig;
+typedef struct _NMIPConfig NMIPConfig;
+typedef struct _NMObject NMObject;
+typedef struct _NMRemoteConnection NMRemoteConnection;
+typedef struct _NMVpnConnection NMVpnConnection;
+typedef struct _NMWimaxNsp NMWimaxNsp;
#endif /* NM_TYPES_H */
diff --git a/man/nm-openvswitch.xml b/man/nm-openvswitch.xml
new file mode 100644
index 0000000000..5573046c38
--- /dev/null
+++ b/man/nm-openvswitch.xml
@@ -0,0 +1,204 @@
+<?xml version='1.0'?>
+<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "common.ent" >
+%entities;
+]>
+
+<!--
+ nmcli-examples(7) manual page
+
+ Copyright 2017 Red Hat, Inc.
+
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. You may obtain a copy of the GNU Free Documentation License
+ from the Free Software Foundation by visiting their Web site or by
+ writing to:
+
+ Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+-->
+
+<refentry id="nm-openvswitch">
+ <refentryinfo>
+ <title>nm-openvswitch</title>
+ <author>NetworkManager OpenVSwitch support</author>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>nm-openvswitch</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo class="source">NetworkManager</refmiscinfo>
+ <refmiscinfo class="manual">OpenVSwitch support overview</refmiscinfo>
+ <refmiscinfo class="version">&NM_VERSION;</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>nm-openvswitch</refname>
+ <refpurpose>overview of NetworkManager OpenVSwitch support</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Overview</title>
+
+ <para>NetworkManager includes basic OpenVSwitch support, good enough
+ to be capable of setting up simple OpenVSwitch configurations. It is not
+ extensive and does not expose all functionality of OpenVSwitch provides.
+ For large or complicated deployments users are advised to use native tools
+ shipped with OpenVSwitch. This document seeks to provide overview of
+ functionality currently provided by NetworkManager, its capabilities and
+ limitations.</para>
+
+ <para>First and foremost: NetworkManager applies the configuration by
+ modifying the OVSDB directly. Its configuration model follows the OVSDB
+ database model closely and it does not provide the level of abstraction
+ <command>ovs-vsctl</command> provides.</para>
+
+ <para>In practical terms it means the following:
+ <itemizedlist>
+ <listitem>
+ <para>NetworkManager only ever talks to a single OVSDB instance via an
+ UNIX domain socket.</para>
+ </listitem>
+ <listitem>
+ <para>The configuration is made up of Bridges, Ports and
+ Interfaces. Interfaces are always enslaved to Ports, and Ports are always
+ enslaved to Bridges.</para>
+ </listitem>
+ <listitem>
+ <para>NetworkManager only creates Bridges, Ports and Interfaces
+ you ask it to. Unlike <command>ovs-vsctl</command>, it doesn't create the
+ local interface nor its port automatically.</para>
+ </listitem>
+ <listitem>
+ <para>You can't enslave Interface directly to a Bridge. You
+ always need a Port, even if it has just one interface.</para>
+ </listitem>
+ <listitem>
+ <para>There are no VLANs. The VLAN tagging is enabled by setting a
+ <link linkend="nm-settings.property.ovs-port.tag">ovs-port.tag</link>
+ property on a Port.</para>
+ </listitem>
+ <listitem>
+ <para>There are no bonds either. The bonding is enabled by
+ enslaving multiple Interfaces to a Port and configured by setting
+ properties on a port.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <refsect2>
+ <title>Bridges</title>
+
+ <para>Bridges are represented by connections of ovs-bridge
+ <link linkend="nm-settings.property.connection.type">type</link>.
+ Due to the limitations of OVSDB, "empty" Bridges (with no Ports) can't exist.
+ NetworkManager inserts the records for Bridges into OVSDB when a Port is
+ enslaved.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title>Ports</title>
+
+ <para>Ports are represented by connections of ovs-port
+ <link linkend="nm-settings.property.connection.type">type</link>.
+ Due to the limitations of OVSDB, "empty" Ports (with no Interfaces) can't
+ exist. Ports can also be configured to do VLAN tagging or Bonding.
+ NetworkManager inserts the records for Ports into OVSDB when an Interface is
+ enslaved. Ports must be enslaved to a Bridge.</para>
+ </refsect2>
+
+ <refsect2>
+ <title>Interfaces</title>
+
+ <para>Interfaces are represented by a connections enslaved to a Port. The
+ system interfaces (that have a corresponding Linux link) have a respective
+ <link linkend="nm-settings.property.connection.type">connection.type</link>
+ of the link (e.g. "wired", "bond", "dummy", etc.). Other interfaces ("internal"
+ or "patch" interfaces) are of ovs-interface type. The OVSDB entries are
+ inserted upon enslavement to a Port.</para>
+ </refsect2>
+ </refsect1>
+
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example><title>Creating a Bridge with a single internal Interface</title>
+<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-bridge conn.interface bridge0</userinput>
+Connection 'ovs-bridge-bridge0' (d10fc64d-1d48-4394-a1b8-e1aea72f27d5) successfully added.
+<prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface port0 conn.master bridge0</userinput>
+Connection 'ovs-port-port0' (5ae22bae-bba4-4815-9ade-7e635633e1f0) successfully added.
+<prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-interface conn.interface iface0 conn.master port0 \
+ ipv4.method manual ipv4.address 192.0.2.1/24</userinput>
+Connection 'ovs-interface-iface0' (3640d2a1-a2fd-4718-92f1-cffadb5b6cdc) successfully added.
+</screen>
+ <para>As said above, you need to create a Port even for a single interface.
+ Also, before you add the Interface, the Bridge and Port devices appear active,
+ but are not configured in OVSDB yet. You can inspect the results with
+ <command>ovs-vsctl show</command>.</para>
+ </example>
+
+ <example><title>Adding a Linux interface to a Bridge</title>
+<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface port1 conn.master bridge0</userinput>
+Connection 'ovs-port-port1' (67d041eb-8e7b-4458-afee-a1d07c9c4552) successfully added.
+<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth0 conn.master port1</userinput>
+Connection 'ovs-slave-eth0' (d459c45c-cf78-4c1c-b4b7-505e71379624) successfully added.
+</screen>
+<para>Again, you need a port.</para>
+ </example>
+
+ <example><title>Creating a VLAN</title>
+<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface port2 conn.master bridge0 ovs-port.tag 120</userinput>
+Connection 'ovs-port-port2' (3994c093-4ef7-4549-a4fd-627b831c3cb8) successfully added.
+<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth1 conn.master port2</userinput>
+Connection 'ovs-slave-eth1' (099be06e-71ad-484d-8d5a-fcadc5f207f5) successfully added.
+</screen>
+ <para>It's just a port with a tag.</para>
+ </example>
+
+ <example><title>Creating a Bond</title>
+<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface bond0 conn.master bridge0</userinput>
+Connection 'ovs-port-bond0' (d154ebf9-e999-4e1b-a084-a3de53d25d8a) successfully added.
+<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth2 conn.master bond0</userinput>
+Connection 'ovs-slave-eth2' (475ac1bf-30b2-4534-a877-27f33f58b082) successfully added.
+<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth3 conn.master bond0</userinput>
+Connection 'ovs-slave-eth3' (8dedeecb-ed12-482b-b77a-24a4fb835136) successfully added.
+</screen>
+ <para>It's just a Port with multiple interfaces. See nm-settings manual for
+ Bonding options you can use with "nmcli c add" or "nmcli c modify". You could
+ even set a VLAN tag on the same Port to do VLAN tagging and bonding at the same
+ time.</para>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>Bugs</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Not all OpenVSwitch capabilities are supported.</para>
+ </listitem>
+ <listitem>
+ <para>OpenVSwitch devices don't expose many useful properties on D-Bus.</para>
+ </listitem>
+ </itemizedlist>
+ <para>Probably many more.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <ulink url="https://www.rfc-editor.org/rfc/rfc7047.txt">RFC 7047: The Open vSwitch Database Management Protocol</ulink>,
+ <citerefentry><refentrytitle>ovs-vsctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>ovs-vswitchd.conf.db</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <link linkend='nm-settings'><citerefentry><refentrytitle>nm-settings</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>,
+ <link linkend='nmcli'><citerefentry><refentrytitle>nmcli</refentrytitle><manvolnum>1</manvolnum></citerefentry></link>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d4dfc496bb..c86a0dbdab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -75,6 +75,10 @@ libnm-core/nm-setting-ip-tunnel.c
libnm-core/nm-setting-macsec.c
libnm-core/nm-setting-macvlan.c
libnm-core/nm-setting-olpc-mesh.c
+libnm-core/nm-setting-ovs-interface.c
+libnm-core/nm-setting-ovs-patch.c
+libnm-core/nm-setting-ovs-port.c
+libnm-core/nm-setting-ovs-bridge.c
libnm-core/nm-setting-ppp.c
libnm-core/nm-setting-pppoe.c
libnm-core/nm-setting-proxy.c
@@ -137,6 +141,9 @@ libnm/nm-device-ip-tunnel.c
libnm/nm-device-macvlan.c
libnm/nm-device-modem.c
libnm/nm-device-olpc-mesh.c
+libnm/nm-device-ovs-bridge.c
+libnm/nm-device-ovs-interface.c
+libnm/nm-device-ovs-port.c
libnm/nm-device-team.c
libnm/nm-device-vlan.c
libnm/nm-device-vxlan.c
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index 3730bb8925..734b824246 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -43,6 +43,10 @@
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-patch.h"
+#include "nm-setting-ovs-port.h"
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-proxy.h"
@@ -235,6 +239,26 @@ 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_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_INTERFACE] = {
+ .meta_type = NM_META_SETTING_TYPE_OVS_INTERFACE,
+ .setting_name = NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+ .get_setting_gtype = nm_setting_ovs_interface_get_type,
+ },
+ [NM_META_SETTING_TYPE_OVS_PATCH] = {
+ .meta_type = NM_META_SETTING_TYPE_OVS_PATCH,
+ .setting_name = NM_SETTING_OVS_PATCH_SETTING_NAME,
+ .get_setting_gtype = nm_setting_ovs_patch_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,
.setting_name = NM_SETTING_PPPOE_SETTING_NAME,
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index 949a6920c6..f36e322e89 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -75,6 +75,10 @@ typedef enum {
NM_META_SETTING_TYPE_MACSEC,
NM_META_SETTING_TYPE_MACVLAN,
NM_META_SETTING_TYPE_OLPC_MESH,
+ NM_META_SETTING_TYPE_OVS_BRIDGE,
+ NM_META_SETTING_TYPE_OVS_INTERFACE,
+ NM_META_SETTING_TYPE_OVS_PATCH,
+ NM_META_SETTING_TYPE_OVS_PORT,
NM_META_SETTING_TYPE_PPP,
NM_META_SETTING_TYPE_PPPOE,
NM_META_SETTING_TYPE_PROXY,
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index f7a87aae82..126546ec93 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -1697,6 +1697,63 @@ nmtst_assert_setting_verifies (NMSetting *setting)
g_assert (success);
}
+#if defined(__NM_SIMPLE_CONNECTION_H__)
+static inline void
+_nmtst_assert_connection_has_settings (NMConnection *connection, gboolean has_at_least, gboolean has_at_most, ...)
+{
+ gs_unref_hashtable GHashTable *names = NULL;
+ gs_free NMSetting **settings = NULL;
+ va_list ap;
+ const char *name;
+ guint i, len;
+ gs_unref_ptrarray GPtrArray *names_arr = NULL;
+
+ g_assert (NM_IS_CONNECTION (connection));
+
+ names = g_hash_table_new (g_str_hash, g_str_equal);
+ names_arr = g_ptr_array_new ();
+
+ va_start (ap, has_at_most);
+ while ((name = va_arg (ap, const char *))) {
+ if (!nm_g_hash_table_add (names, (gpointer) name))
+ g_assert_not_reached ();
+ g_ptr_array_add (names_arr, (gpointer) name);
+ }
+ va_end (ap);
+
+ g_ptr_array_add (names_arr, NULL);
+
+ settings = nm_connection_get_settings (connection, &len);
+ for (i = 0; i < len; i++) {
+ if ( !g_hash_table_remove (names, nm_setting_get_name (settings[i]))
+ && has_at_most) {
+ g_error ("nmtst_assert_connection_has_settings(): has setting \"%s\" which is not expected",
+ nm_setting_get_name (settings[i]));
+ }
+ }
+ if ( g_hash_table_size (names) > 0
+ && has_at_least) {
+ gs_free char *expected_str = g_strjoinv (" ", (char **) names_arr->pdata);
+ gs_free const char **settings_names = NULL;
+ gs_free char *has_str = NULL;
+
+ settings_names = g_new0 (const char *, len + 1);
+ for (i = 0; i < len; i++)
+ settings_names[i] = nm_setting_get_name (settings[i]);
+ has_str = g_strjoinv (" ", (char **) settings_names);
+
+ g_error ("nmtst_assert_connection_has_settings(): the setting lacks %u expected settings (expected: [%s] vs. has: [%s])",
+ g_hash_table_size (names),
+ expected_str,
+ has_str);
+ }
+}
+#define nmtst_assert_connection_has_settings(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE, TRUE, __VA_ARGS__, NULL)
+#define nmtst_assert_connection_has_settings_at_least(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE, FALSE, __VA_ARGS__, NULL)
+#define nmtst_assert_connection_has_settings_at_most(connection, ...) _nmtst_assert_connection_has_settings ((connection), FALSE, TRUE, __VA_ARGS__, NULL)
+
+#endif /* __NM_SIMPLE_CONNECTION_H__ */
+
static inline void
nmtst_assert_setting_verify_fails (NMSetting *setting,
GQuark expect_error_domain,
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index b7a2faaa17..596c5bb362 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -175,32 +175,24 @@ nm_utils_get_ip_config_method (NMConnection *connection,
if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) {
g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
- if (nm_setting_connection_get_master (s_con))
+ s_ip4 = nm_connection_get_setting_ip4_config (connection);
+ if (!s_ip4)
return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
- else {
- s_ip4 = nm_connection_get_setting_ip4_config (connection);
- if (!s_ip4)
- return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
- method = nm_setting_ip_config_get_method (s_ip4);
- g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
-
- return method;
- }
+ method = nm_setting_ip_config_get_method (s_ip4);
+ g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+ return method;
} else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) {
g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
- if (nm_setting_connection_get_master (s_con))
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ if (!s_ip6)
return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
- else {
- s_ip6 = nm_connection_get_setting_ip6_config (connection);
- if (!s_ip6)
- return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
- method = nm_setting_ip_config_get_method (s_ip6);
- g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
-
- return method;
- }
+ method = nm_setting_ip_config_get_method (s_ip6);
+ g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+
+ return method;
} else
g_assert_not_reached ();
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index f0c7e59134..5f2e7d0da6 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -40,13 +40,16 @@ _nm_device_log_self_to_device (t *self) \
\
if (nm_logging_enabled (_level, _domain)) { \
typeof (*self) *const _self = (self); \
- const char *const _ifname = _nm_device_get_iface (_nm_device_log_self_to_device (_self)); \
+ NMDevice *_device = _nm_device_log_self_to_device (_self); \
+ const char *const _ifname = _nm_device_get_iface (_device); \
+ const char *const _type_description = _ifname ? nm_device_get_type_description (_device) : NULL; \
\
nm_log_obj (_level, _domain, \
_ifname, NULL, \
_self, "device", \
- "%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- NM_PRINT_FMT_QUOTED (_ifname, "(", _ifname, ")", "[null]") \
+ "%s%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ NM_PRINT_FMT_QUOTED (_ifname, "(", _ifname, "", "[null]"), \
+ NM_PRINT_FMT_QUOTED (_ifname, ",", _type_description, ")", "") \
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} G_STMT_END
diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c
index 09b25f50e2..8b3968d513 100644
--- a/src/devices/nm-device-ppp.c
+++ b/src/devices/nm-device-ppp.c
@@ -117,7 +117,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
return;
}
if (renamed)
- nm_manager_remove_device (nm_manager_get (), iface);
+ nm_manager_remove_device (nm_manager_get (), iface, NM_DEVICE_TYPE_PPP);
nm_device_activate_schedule_ip4_config_result (device, config);
return;
@@ -195,7 +195,7 @@ act_stage3_ip4_config_start (NMDevice *device,
if (!nm_device_take_over_link (device, priv->pending_ifname, &renamed))
return NM_ACT_STAGE_RETURN_FAILURE;
if (renamed)
- nm_manager_remove_device (nm_manager_get (), priv->pending_ifname);
+ nm_manager_remove_device (nm_manager_get (), priv->pending_ifname, NM_DEVICE_TYPE_PPP);
if (out_config)
*out_config = g_steal_pointer (&priv->pending_ip4_config);
else
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 52d7e3e3d3..c286d4668a 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -646,6 +646,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason,
NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NEW_ACTIVATION, "new-activation"),
NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_CHANGED, "parent-changed"),
NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED, "parent-managed-changed"),
+ NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"),
);
#define reason_to_string(reason) \
@@ -1661,6 +1662,10 @@ _get_route_metric_default (NMDevice *self)
return 700;
case NM_DEVICE_TYPE_BT:
return 750;
+ case NM_DEVICE_TYPE_OVS_BRIDGE:
+ case NM_DEVICE_TYPE_OVS_INTERFACE:
+ case NM_DEVICE_TYPE_OVS_PORT:
+ return 800;
case NM_DEVICE_TYPE_GENERIC:
return 950;
case NM_DEVICE_TYPE_UNKNOWN:
@@ -2538,12 +2543,27 @@ static void
device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMDevice *master;
+ nm_auto_nmpobj const NMPObject *plink_master_keep_alive = NULL;
+ const NMPlatformLink *plink_master;
g_return_if_fail (plink);
if (plink->master <= 0)
return;
+ master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master);
+ plink_master = nm_platform_link_get (nm_device_get_platform (self), plink->master);
+ plink_master_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink_master));
+
+ if ( master == NULL
+ && plink_master
+ && g_strcmp0 (plink_master->name, "ovs-system") == 0
+ && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) {
+ _LOGD (LOGD_DEVICE, "the device claimed by openvswitch");
+ return;
+ }
+
if (priv->master) {
if ( plink->master > 0
&& plink->master == nm_device_get_ifindex (priv->master)) {
@@ -2555,20 +2575,16 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink)
nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
}
- if (plink->master > 0) {
- NMDevice *master;
- master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master);
- if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave)
- nm_device_master_add_slave (master, self, FALSE);
- else if (master) {
- _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring",
- nm_device_get_iface (master));
- } else {
- _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
- plink->master,
- nm_platform_link_get_name (nm_device_get_platform (self), plink->master));
- }
+ if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave)
+ nm_device_master_add_slave (master, self, FALSE);
+ else if (master) {
+ _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring",
+ nm_device_get_iface (master));
+ } else {
+ _LOGW (LOGD_DEVICE, "enslaved to unknown device %d (%s%s%s)",
+ plink->master,
+ NM_PRINT_FMT_QUOTED (plink_master, "\"", plink_master->name, "\"", "??"));
}
}
@@ -2650,8 +2666,7 @@ device_link_changed (NMDevice *self)
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean ip_ifname_changed = FALSE;
- const char *udi;
- NMPlatformLink info;
+ nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL;
const NMPlatformLink *pllink;
int ifindex;
gboolean was_up;
@@ -2665,37 +2680,20 @@ device_link_changed (NMDevice *self)
if (!pllink)
return G_SOURCE_REMOVE;
- info = *pllink;
-
- udi = nm_platform_link_get_udi (nm_device_get_platform (self), info.ifindex);
- if (udi && !nm_streq0 (udi, priv->udi)) {
- /* Update UDI to what udev gives us */
- g_free (priv->udi);
- priv->udi = g_strdup (udi);
- _notify (self, PROP_UDI);
- }
-
- if (!nm_streq0 (info.driver, priv->driver)) {
- g_free (priv->driver);
- priv->driver = g_strdup (info.driver);
- _notify (self, PROP_DRIVER);
- }
-
- _set_mtu (self, info.mtu);
+ pllink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (pllink));
- if (ifindex == nm_device_get_ip_ifindex (self))
- _stats_update_counters_from_pllink (self, &info);
+ nm_device_update_from_platform_link (self, pllink);
had_hw_addr = (priv->hw_addr != NULL);
nm_device_update_hw_address (self);
got_hw_addr = (!had_hw_addr && priv->hw_addr);
nm_device_update_permanent_hw_address (self, FALSE);
- if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
+ if (pllink->name[0] && strcmp (priv->iface, pllink->name) != 0) {
_LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'",
- priv->ifindex, priv->iface, info.name);
+ priv->ifindex, priv->iface, pllink->name);
g_free (priv->iface);
- priv->iface = g_strdup (info.name);
+ priv->iface = g_strdup (pllink->name);
/* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */
ip_ifname_changed = !priv->ip_iface;
@@ -2718,8 +2716,8 @@ device_link_changed (NMDevice *self)
nm_device_emit_recheck_auto_activate (self);
}
- if (priv->ndisc && info.inet6_token.id) {
- if (nm_ndisc_set_iid (priv->ndisc, info.inet6_token))
+ if (priv->ndisc && pllink->inet6_token.id) {
+ if (nm_ndisc_set_iid (priv->ndisc, pllink->inet6_token))
_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
}
@@ -2728,16 +2726,16 @@ device_link_changed (NMDevice *self)
&& !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
nm_device_set_carrier (self, pllink->connected);
- klass->link_changed (self, &info);
+ klass->link_changed (self, pllink);
/* Update DHCP, etc, if needed */
if (ip_ifname_changed)
nm_device_update_dynamic_ip_setup (self);
was_up = priv->up;
- priv->up = NM_FLAGS_HAS (info.n_ifi_flags, IFF_UP);
+ priv->up = NM_FLAGS_HAS (pllink->n_ifi_flags, IFF_UP);
- if ( info.initialized
+ if ( pllink->initialized
&& nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
NMDeviceStateReason reason;
@@ -2767,7 +2765,7 @@ device_link_changed (NMDevice *self)
set_unmanaged_external_down (self, FALSE);
- device_recheck_slave_status (self, &info);
+ device_recheck_slave_status (self, pllink);
if (priv->up && !was_up) {
/* the link was down and just came up. That happens for example, while changing MTU.
@@ -3137,38 +3135,54 @@ nm_device_create_and_realize (NMDevice *self,
return TRUE;
}
-static void
-update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
+void
+nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- const char *udi;
+ const char *str;
+ int ifindex;
+ guint32 mtu;
- g_return_if_fail (plink != NULL);
+ g_return_if_fail (plink == NULL || link_type_compatible (self, plink->type, NULL, NULL));
- udi = nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex);
- if (udi && !nm_streq0 (udi, priv->udi)) {
+ str = plink ? nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex) : NULL;
+ if (g_strcmp0 (str, priv->udi)) {
g_free (priv->udi);
- priv->udi = g_strdup (udi);
+ priv->udi = g_strdup (str);
_notify (self, PROP_UDI);
}
- if (!g_strcmp0 (plink->name, priv->iface)) {
+ str = plink ? plink->name : NULL;
+ if (str && g_strcmp0 (str, priv->iface)) {
g_free (priv->iface);
- priv->iface = g_strdup (plink->name);
+ priv->iface = g_strdup (str);
_notify (self, PROP_IFACE);
}
- if (priv->ifindex != plink->ifindex) {
- priv->ifindex = plink->ifindex;
- _notify (self, PROP_IFINDEX);
- }
-
- priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP);
- if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) {
+ str = plink ? plink->driver : NULL;
+ if (g_strcmp0 (str, priv->driver) != 0) {
g_free (priv->driver);
- priv->driver = g_strdup (plink->driver);
+ priv->driver = g_strdup (str);
_notify (self, PROP_DRIVER);
}
+
+ if (plink) {
+ priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP);
+ if (plink->ifindex == nm_device_get_ip_ifindex (self))
+ _stats_update_counters_from_pllink (self, plink);
+ } else {
+ priv->up = FALSE;
+ }
+
+ mtu = plink ? plink->mtu : 0;
+ _set_mtu (self, mtu);
+
+ ifindex = plink ? plink->ifindex : 0;
+ if (priv->ifindex != ifindex) {
+ priv->ifindex = ifindex;
+ _notify (self, PROP_IFINDEX);
+ NM_DEVICE_GET_CLASS (self)->link_changed (self, plink);
+ }
}
static void
@@ -3282,11 +3296,8 @@ realize_start_setup (NMDevice *self,
nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
- if (plink) {
- g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL));
- update_device_from_platform_link (self, plink);
- _stats_update_counters_from_pllink (self, plink);
- }
+ if (plink)
+ nm_device_update_from_platform_link (self, plink);
if (priv->ifindex > 0) {
priv->physical_port_id = nm_platform_link_get_physical_port_id (nm_device_get_platform (self), priv->ifindex);
@@ -3883,6 +3894,113 @@ nm_device_get_master (NMDevice *self)
return NULL;
}
+static gboolean
+get_ip_config_may_fail (NMDevice *self, int addr_family)
+{
+ NMConnection *connection;
+ NMSettingIPConfig *s_ip = NULL;
+
+ connection = nm_device_get_applied_connection (self);
+
+ /* Fail the connection if the failed IP method is required to complete */
+ switch (addr_family) {
+ case AF_INET:
+ s_ip = nm_connection_get_setting_ip4_config (connection);
+ break;
+ case AF_INET6:
+ s_ip = nm_connection_get_setting_ip6_config (connection);
+ break;
+ default:
+ nm_assert_not_reached ();
+ }
+
+ return !s_ip || nm_setting_ip_config_get_may_fail (s_ip);
+}
+
+/*
+ * check_ip_state
+ *
+ * Transition the device from IP_CONFIG to the next state according to the
+ * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are
+ * called just after the initial configuration and thus IPv4/IPv6 are allowed to
+ * fail if the ipvx.may-fail properties say so, because the IP methods couldn't
+ * even be started.
+ */
+static void
+check_ip_state (NMDevice *self, gboolean may_fail)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gboolean ip4_disabled = FALSE, ip6_ignore = FALSE;
+ NMSettingIPConfig *s_ip4, *s_ip6;
+ NMDeviceState state;
+
+ if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG)
+ return;
+
+ /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the
+ * master to enslave us. */
+ if ( nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))
+ && !priv->is_enslaved)
+ return;
+
+ s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG);
+ if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4),
+ NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
+ ip4_disabled = TRUE;
+
+ s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
+ if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6),
+ NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
+ ip6_ignore = TRUE;
+
+ if ( priv->ip4_state == IP_DONE
+ && priv->ip6_state == IP_DONE) {
+ /* Both method completed (or disabled), proceed with activation */
+ nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
+ return;
+ }
+
+ if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE))
+ && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) {
+ /* Either both methods failed, or only one failed and the other is
+ * disabled */
+ if (nm_device_sys_iface_state_is_external_or_assume (self)) {
+ /* We have assumed configuration, but couldn't redo it. No problem,
+ * move to check state. */
+ _set_ip_state (self, AF_INET, IP_DONE);
+ _set_ip_state (self, AF_INET6, IP_DONE);
+ state = NM_DEVICE_STATE_IP_CHECK;
+ } else if ( may_fail
+ && get_ip_config_may_fail (self, AF_INET)
+ && get_ip_config_may_fail (self, AF_INET6)) {
+ /* Couldn't start either IPv6 and IPv4 autoconfiguration,
+ * but both are allowed to fail. */
+ state = NM_DEVICE_STATE_SECONDARIES;
+ } else {
+ /* Autoconfiguration attempted without success. */
+ state = NM_DEVICE_STATE_FAILED;
+ }
+
+ nm_device_state_changed (self,
+ state,
+ NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+ return;
+ }
+
+ /* If a method is still pending but required, wait */
+ if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET))
+ return;
+ if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6))
+ return;
+
+ /* If at least a method has completed, proceed with activation */
+ if ( (priv->ip4_state == IP_DONE && !ip4_disabled)
+ || (priv->ip6_state == IP_DONE && !ip6_ignore)) {
+ nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
+ return;
+ }
+}
+
/**
* nm_device_slave_notify_enslave:
* @self: the slave device
@@ -3919,10 +4037,8 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
}
if (activating) {
- _set_ip_state (self, AF_INET, IP_DONE);
- _set_ip_state (self, AF_INET6, IP_DONE);
if (success)
- nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
+ check_ip_state (self, FALSE);
else
nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN);
} else
@@ -4893,29 +5009,6 @@ activation_source_is_scheduled (NMDevice *self,
/*****************************************************************************/
-static gboolean
-get_ip_config_may_fail (NMDevice *self, int addr_family)
-{
- NMConnection *connection;
- NMSettingIPConfig *s_ip = NULL;
-
- connection = nm_device_get_applied_connection (self);
-
- /* Fail the connection if the failed IP method is required to complete */
- switch (addr_family) {
- case AF_INET:
- s_ip = nm_connection_get_setting_ip4_config (connection);
- break;
- case AF_INET6:
- s_ip = nm_connection_get_setting_ip6_config (connection);
- break;
- default:
- nm_assert_not_reached ();
- }
-
- return !s_ip || nm_setting_ip_config_get_may_fail (s_ip);
-}
-
static void
master_ready (NMDevice *self,
NMActiveConnection *active)
@@ -5210,84 +5303,6 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
activation_source_schedule (self, activate_stage2_device_config, AF_INET);
}
-/*
- * check_ip_state
- *
- * Transition the device from IP_CONFIG to the next state according to the
- * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are
- * called just after the initial configuration and thus IPv4/IPv6 are allowed to
- * fail if the ipvx.may-fail properties say so, because the IP methods couldn't
- * even be started.
- */
-static void
-check_ip_state (NMDevice *self, gboolean may_fail)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- gboolean ip4_disabled = FALSE, ip6_ignore = FALSE;
- NMSettingIPConfig *s_ip4, *s_ip6;
- NMDeviceState state;
-
- if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG)
- return;
-
- s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG);
- if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4),
- NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
- ip4_disabled = TRUE;
-
- s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
- if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6),
- NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
- ip6_ignore = TRUE;
-
- if ( priv->ip4_state == IP_DONE
- && priv->ip6_state == IP_DONE) {
- /* Both method completed (or disabled), proceed with activation */
- nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
- return;
- }
-
- if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE))
- && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) {
- /* Either both methods failed, or only one failed and the other is
- * disabled */
- if (nm_device_sys_iface_state_is_external_or_assume (self)) {
- /* We have assumed configuration, but couldn't redo it. No problem,
- * move to check state. */
- _set_ip_state (self, AF_INET, IP_DONE);
- _set_ip_state (self, AF_INET6, IP_DONE);
- state = NM_DEVICE_STATE_IP_CHECK;
- } else if ( may_fail
- && get_ip_config_may_fail (self, AF_INET)
- && get_ip_config_may_fail (self, AF_INET6)) {
- /* Couldn't start either IPv6 and IPv4 autoconfiguration,
- * but both are allowed to fail. */
- state = NM_DEVICE_STATE_SECONDARIES;
- } else {
- /* Autoconfiguration attempted without success. */
- state = NM_DEVICE_STATE_FAILED;
- }
-
- nm_device_state_changed (self,
- state,
- NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
- return;
- }
-
- /* If a method is still pending but required, wait */
- if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET))
- return;
- if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6))
- return;
-
- /* If at least a method has completed, proceed with activation */
- if ( (priv->ip4_state == IP_DONE && !ip4_disabled)
- || (priv->ip6_state == IP_DONE && !ip6_ignore)) {
- nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
- return;
- }
-}
-
void
nm_device_ip_method_failed (NMDevice *self,
int addr_family,
@@ -6249,9 +6264,16 @@ static gboolean
connection_requires_carrier (NMConnection *connection)
{
NMSettingIPConfig *s_ip4, *s_ip6;
+ NMSettingConnection *s_con;
gboolean ip4_carrier_wanted, ip6_carrier_wanted;
gboolean ip4_used = FALSE, ip6_used = FALSE;
+ /* We can progress to IP_CONFIG now, so that we're enslaved.
+ * That may actually cause carrier to go up and thus continue acivation. */
+ s_con = nm_connection_get_setting_connection (connection);
+ if (nm_setting_connection_get_master (s_con))
+ return FALSE;
+
ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used);
if (ip4_carrier_wanted) {
/* If IPv4 wants a carrier and cannot fail, the whole connection
@@ -8009,12 +8031,6 @@ nm_device_activate_stage3_ip4_start (NMDevice *self)
g_assert (priv->ip4_state == IP_WAIT);
- /* Slaves stay in IP_CONFIG state until master is ready, and then
- * they go directly to SECONDARIES without configuring IPv4.
- */
- if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)))
- return TRUE;
-
_set_ip_state (self, AF_INET, IP_CONF);
ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason);
if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
@@ -8056,12 +8072,6 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
g_assert (priv->ip6_state == IP_WAIT);
- /* Slaves stay in IP_CONFIG state until master is ready, and then
- * they go directly to SECONDARIES without configuring IPv6.
- */
- if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)))
- return TRUE;
-
_set_ip_state (self, AF_INET6, IP_CONF);
ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason);
if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
@@ -8100,10 +8110,6 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
static void
activate_stage3_ip_config_start (NMDevice *self)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- NMActiveConnection *master;
- NMDevice *master_device;
-
_set_ip_state (self, AF_INET, IP_WAIT);
_set_ip_state (self, AF_INET6, IP_WAIT);
@@ -8116,26 +8122,6 @@ activate_stage3_ip_config_start (NMDevice *self)
if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)))
_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
- /* If the device is a slave, then we don't do any IP configuration but we
- * use the IP config stage to indicate to the master we're ready for
- * enslavement. If the master is already activating, it will have tried to
- * enslave us when we changed state to IP_CONFIG, causing us to queue a
- * transition to SECONDARIES (or FAILED if the enslavement failed), with
- * our IP states set to IP_DONE either way. If the master isn't yet
- * activating, then they'll still be in IP_WAIT. Either way, we bail out
- * of IP config here.
- */
- master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request));
- if (master) {
- master_device = nm_active_connection_get_device (master);
- if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) {
- _LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'",
- nm_connection_get_id (nm_device_get_applied_connection (self)),
- master_device ? nm_device_get_iface (master_device) : "(unknown)");
- }
- return;
- }
-
/* IPv4 */
if ( nm_device_activate_ip4_state_in_wait (self)
&& !nm_device_activate_stage3_ip4_start (self))
@@ -8901,7 +8887,7 @@ delete_on_deactivate_link_delete (gpointer user_data)
if (!nm_device_unrealize (data->device, TRUE, &error))
_LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message);
- } else
+ } else if (data->ifindex > 0)
nm_platform_link_delete (nm_device_get_platform (self), data->ifindex);
g_free (data);
@@ -8932,8 +8918,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
DeleteOnDeactivateData *data;
- if (ifindex <= 0)
- return;
if (!priv->nm_owned)
return;
if (priv->queued_act_request)
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index ef83f1b53e..921db8aa5e 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -660,6 +660,9 @@ gboolean nm_device_unrealize (NMDevice *device,
gboolean remove_resources,
GError **error);
+void nm_device_update_from_platform_link (NMDevice *self,
+ const NMPlatformLink *plink);
+
gboolean nm_device_get_autoconnect (NMDevice *device);
void nm_device_set_autoconnect_intern (NMDevice *device, gboolean autoconnect);
void nm_device_emit_recheck_auto_activate (NMDevice *device);
diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c
new file mode 100644
index 0000000000..53ea2b8287
--- /dev/null
+++ b/src/devices/ovs/nm-device-ovs-bridge.c
@@ -0,0 +1,156 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-ovs-bridge.h"
+#include "nm-device-ovs-port.h"
+#include "nm-ovsdb.h"
+
+#include "devices/nm-device-private.h"
+#include "nm-active-connection.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ovs-bridge.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h"
+
+#include "devices/nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceOvsBridge);
+
+/*****************************************************************************/
+
+struct _NMDeviceOvsBridge {
+ NMDevice parent;
+};
+
+struct _NMDeviceOvsBridgeClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ return "ovs-bridge";
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+ NMConnection *connection,
+ NMDevice *parent,
+ const NMPlatformLink **out_plink,
+ GError **error)
+{
+ /* The actual backing resources will be created on enslavement by the port
+ * when it can identify the port and the bridge. */
+
+ return TRUE;
+}
+
+static gboolean
+unrealize (NMDevice *device, GError **error)
+{
+ return TRUE;
+}
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *device)
+{
+ return NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ const char *connection_type;
+
+ if (!NM_DEVICE_CLASS (nm_device_ovs_bridge_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ connection_type = nm_connection_get_connection_type (connection);
+ if (!nm_streq0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
+ return FALSE;
+
+ return TRUE;
+}
+
+static NMActStageReturn
+act_stage3_ip4_config_start (NMDevice *device,
+ NMIP4Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ return NM_ACT_STAGE_RETURN_IP_FAIL;
+}
+
+static NMActStageReturn
+act_stage3_ip6_config_start (NMDevice *device,
+ NMIP6Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ return NM_ACT_STAGE_RETURN_IP_FAIL;
+}
+
+static gboolean
+enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure)
+{
+ if (!configure)
+ return TRUE;
+
+ if (!NM_IS_DEVICE_OVS_PORT (slave))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+release_slave (NMDevice *device, NMDevice *slave, gboolean configure)
+{
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_ovs_bridge_init (NMDeviceOvsBridge *self)
+{
+}
+
+static void
+nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+ device_class->connection_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
+ device_class->is_master = TRUE;
+ device_class->get_type_description = get_type_description;
+ device_class->create_and_realize = create_and_realize;
+ device_class->unrealize = unrealize;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->check_connection_compatible = check_connection_compatible;
+ device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+ device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+ device_class->enslave_slave = enslave_slave;
+ device_class->release_slave = release_slave;
+
+ nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+ NMDBUS_TYPE_DEVICE_OVS_BRIDGE_SKELETON,
+ NULL);
+}
diff --git a/src/devices/ovs/nm-device-ovs-bridge.h b/src/devices/ovs/nm-device-ovs-bridge.h
new file mode 100644
index 0000000000..631b4754ca
--- /dev/null
+++ b/src/devices/ovs/nm-device-ovs-bridge.h
@@ -0,0 +1,35 @@
+/* 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_DEVICE_OVS_BRIDGE_H__
+#define __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__
+
+#define NM_TYPE_DEVICE_OVS_BRIDGE (nm_device_ovs_bridge_get_type ())
+#define NM_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge))
+#define NM_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass))
+#define NM_IS_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_BRIDGE))
+#define NM_IS_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_BRIDGE))
+#define NM_DEVICE_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass))
+
+typedef struct _NMDeviceOvsBridge NMDeviceOvsBridge;
+typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass;
+
+GType nm_device_ovs_bridge_get_type (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ */
diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c
new file mode 100644
index 0000000000..426521c52c
--- /dev/null
+++ b/src/devices/ovs/nm-device-ovs-interface.c
@@ -0,0 +1,191 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-ovs-interface.h"
+#include "nm-ovsdb.h"
+
+#include "devices/nm-device-private.h"
+#include "nm-active-connection.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-port.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h"
+
+#include "devices/nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceOvsInterface);
+
+/*****************************************************************************/
+
+struct _NMDeviceOvsInterface {
+ NMDevice parent;
+};
+
+struct _NMDeviceOvsInterfaceClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ return "ovs-interface";
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+ NMConnection *connection,
+ NMDevice *parent,
+ const NMPlatformLink **out_plink,
+ GError **error)
+{
+ /* The actual backing resources will be created once an interface is
+ * added to a port of ours, since there can be neither an empty port nor
+ * an empty bridge. */
+
+ return TRUE;
+}
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *device)
+{
+ return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+ return TRUE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ NMSettingOvsInterface *s_ovs_iface;
+
+ if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ s_ovs_iface = nm_connection_get_setting_ovs_interface (connection);
+ if (!s_ovs_iface)
+ return FALSE;
+ if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface),
+ "internal", "patch")) {
+ return FALSE;
+ }
+
+ s_con = nm_connection_get_setting_connection (connection);
+ if (g_strcmp0 (nm_setting_connection_get_connection_type (s_con),
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME) != 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+link_changed (NMDevice *device,
+ const NMPlatformLink *pllink)
+{
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
+ nm_device_bring_up (device, TRUE, NULL);
+ nm_device_activate_schedule_stage3_ip_config_start (device);
+ }
+}
+
+static gboolean
+_is_internal_interface (NMDevice *device)
+{
+ NMConnection *connection = nm_device_get_applied_connection (device);
+ NMSettingOvsInterface *s_ovs_iface = nm_connection_get_setting_ovs_interface (connection);
+
+ g_return_val_if_fail (s_ovs_iface, FALSE);
+
+ return strcmp (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal") == 0;
+}
+
+static NMActStageReturn
+act_stage3_ip4_config_start (NMDevice *device,
+ NMIP4Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ if (!_is_internal_interface (device))
+ return NM_ACT_STAGE_RETURN_IP_FAIL;
+
+ if (!nm_device_get_ip_ifindex (device))
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+
+ return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason);
+}
+
+static NMActStageReturn
+act_stage3_ip6_config_start (NMDevice *device,
+ NMIP6Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ if (!_is_internal_interface (device))
+ return NM_ACT_STAGE_RETURN_IP_FAIL;
+
+ if (!nm_device_get_ip_ifindex (device))
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+
+ return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason);
+}
+
+static gboolean
+can_unmanaged_external_down (NMDevice *self)
+{
+ return FALSE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_ovs_interface_init (NMDeviceOvsInterface *self)
+{
+}
+
+static void
+nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+ NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH);
+
+ device_class->connection_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
+ device_class->get_type_description = get_type_description;
+ device_class->create_and_realize = create_and_realize;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->is_available = is_available;
+ device_class->check_connection_compatible = check_connection_compatible;
+ device_class->link_changed = link_changed;
+ device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+ device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+ device_class->can_unmanaged_external_down = can_unmanaged_external_down;
+
+ nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+ NMDBUS_TYPE_DEVICE_OVS_INTERFACE_SKELETON,
+ NULL);
+}
diff --git a/src/devices/ovs/nm-device-ovs-interface.h b/src/devices/ovs/nm-device-ovs-interface.h
new file mode 100644
index 0000000000..a748e206e3
--- /dev/null
+++ b/src/devices/ovs/nm-device-ovs-interface.h
@@ -0,0 +1,35 @@
+/* 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_DEVICE_OVS_INTERFACE_H__
+#define __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__
+
+#define NM_TYPE_DEVICE_OVS_INTERFACE (nm_device_ovs_interface_get_type ())
+#define NM_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface))
+#define NM_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass))
+#define NM_IS_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_INTERFACE))
+#define NM_IS_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_INTERFACE))
+#define NM_DEVICE_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass))
+
+typedef struct _NMDeviceOvsInterface NMDeviceOvsInterface;
+typedef struct _NMDeviceOvsInterfaceClass NMDeviceOvsInterfaceClass;
+
+GType nm_device_ovs_interface_get_type (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ */
diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c
new file mode 100644
index 0000000000..83199f2d4c
--- /dev/null
+++ b/src/devices/ovs/nm-device-ovs-port.c
@@ -0,0 +1,201 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-ovs-port.h"
+#include "nm-ovsdb.h"
+
+#include "devices/nm-device-private.h"
+#include "nm-active-connection.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ovs-port.h"
+#include "nm-setting-ovs-port.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.OvsPort.h"
+
+#include "devices/nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceOvsPort);
+
+/*****************************************************************************/
+
+struct _NMDeviceOvsPort {
+ NMDevice parent;
+};
+
+struct _NMDeviceOvsPortClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ return "ovs-port";
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+ NMConnection *connection,
+ NMDevice *parent,
+ const NMPlatformLink **out_plink,
+ GError **error)
+{
+ /* The port will be added to ovsdb when an interface is enslaved,
+ * because there's no such thing like an empty port. */
+
+ return TRUE;
+}
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *device)
+{
+ return NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ const char *connection_type;
+
+ if (!NM_DEVICE_CLASS (nm_device_ovs_port_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ connection_type = nm_setting_connection_get_connection_type (s_con);
+ if (!connection_type)
+ return FALSE;
+
+ if (strcmp (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static NMActStageReturn
+act_stage3_ip4_config_start (NMDevice *device,
+ NMIP4Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ return NM_ACT_STAGE_RETURN_IP_FAIL;
+}
+
+static NMActStageReturn
+act_stage3_ip6_config_start (NMDevice *device,
+ NMIP6Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ return NM_ACT_STAGE_RETURN_IP_FAIL;
+}
+
+static void
+add_iface_cb (GError *error, gpointer user_data)
+{
+ NMDevice *slave = user_data;
+
+ if (error) {
+ nm_log_warn (LOGD_DEVICE, "device %s could not be added to a ovs port: %s",
+ nm_device_get_iface (slave), error->message);
+ nm_device_state_changed (slave,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_OVSDB_FAILED);
+ }
+
+ g_object_unref (slave);
+}
+
+static gboolean
+enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure)
+{
+ NMActiveConnection *ac_port = NULL;
+ NMActiveConnection *ac_bridge = NULL;
+
+ if (!configure)
+ return TRUE;
+
+
+ ac_port = NM_ACTIVE_CONNECTION (nm_device_get_act_request (device));
+ ac_bridge = nm_active_connection_get_master (ac_port);
+ if (!ac_bridge)
+ ac_bridge = ac_port;
+
+ nm_ovsdb_add_interface (nm_ovsdb_get (),
+ nm_active_connection_get_applied_connection (ac_bridge),
+ nm_device_get_applied_connection (device),
+ nm_device_get_applied_connection (slave),
+ add_iface_cb, g_object_ref (slave));
+
+ return TRUE;
+}
+
+static void
+del_iface_cb (GError *error, gpointer user_data)
+{
+ NMDevice *slave = user_data;
+
+ if (error) {
+ nm_log_warn (LOGD_DEVICE, "device %s could not be removed from a ovs port: %s",
+ nm_device_get_iface (slave), error->message);
+ nm_device_state_changed (slave,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_OVSDB_FAILED);
+ }
+
+ g_object_unref (slave);
+}
+
+static void
+release_slave (NMDevice *device, NMDevice *slave, gboolean configure)
+{
+ nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave),
+ del_iface_cb, g_object_ref (slave));
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_ovs_port_init (NMDeviceOvsPort *self)
+{
+}
+
+static void
+nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+ device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME;
+ device_class->is_master = TRUE;
+ device_class->get_type_description = get_type_description;
+ device_class->create_and_realize = create_and_realize;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->check_connection_compatible = check_connection_compatible;
+ device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+ device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+ device_class->enslave_slave = enslave_slave;
+ device_class->release_slave = release_slave;
+
+ nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+ NMDBUS_TYPE_DEVICE_OVS_PORT_SKELETON,
+ NULL);
+}
diff --git a/src/devices/ovs/nm-device-ovs-port.h b/src/devices/ovs/nm-device-ovs-port.h
new file mode 100644
index 0000000000..5ccf1ec195
--- /dev/null
+++ b/src/devices/ovs/nm-device-ovs-port.h
@@ -0,0 +1,35 @@
+/* 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_DEVICE_OVS_PORT_H__
+#define __NETWORKMANAGER_DEVICE_OVS_PORT_H__
+
+#define NM_TYPE_DEVICE_OVS_PORT (nm_device_ovs_port_get_type ())
+#define NM_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort))
+#define NM_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass))
+#define NM_IS_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_PORT))
+#define NM_IS_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_PORT))
+#define NM_DEVICE_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass))
+
+typedef struct _NMDeviceOvsPort NMDeviceOvsPort;
+typedef struct _NMDeviceOvsPortClass NMDeviceOvsPortClass;
+
+GType nm_device_ovs_port_get_type (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_OVS_PORT_H__ */
diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c
new file mode 100644
index 0000000000..830f94fc0c
--- /dev/null
+++ b/src/devices/ovs/nm-ovs-factory.c
@@ -0,0 +1,195 @@
+/* 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-manager.h"
+#include "nm-ovsdb.h"
+#include "nm-device-ovs-interface.h"
+#include "nm-device-ovs-port.h"
+#include "nm-device-ovs-bridge.h"
+#include "platform/nm-platform.h"
+#include "nm-core-internal.h"
+#include "devices/nm-device-factory.h"
+
+/*****************************************************************************/
+
+typedef struct {
+ NMDeviceFactory parent;
+} NMOvsFactory;
+
+typedef struct {
+ NMDeviceFactoryClass parent;
+} NMOvsFactoryClass;
+
+#define NM_TYPE_OVS_FACTORY (nm_ovs_factory_get_type ())
+#define NM_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OVS_FACTORY, NMOvsFactory))
+#define NM_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass))
+#define NM_IS_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OVS_FACTORY))
+#define NM_IS_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVS_FACTORY))
+#define NM_OVS_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass))
+
+static GType nm_ovs_factory_get_type (void);
+G_DEFINE_TYPE (NMOvsFactory, nm_ovs_factory, NM_TYPE_DEVICE_FACTORY)
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_DEVICE
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovs", __VA_ARGS__)
+
+/*****************************************************************************/
+
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+ NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH)
+ NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+ NM_SETTING_OVS_PORT_SETTING_NAME)
+)
+
+G_MODULE_EXPORT NMDeviceFactory *
+nm_device_factory_create (GError **error)
+{
+ return (NMDeviceFactory *) g_object_new (NM_TYPE_OVS_FACTORY, NULL);
+}
+
+static NMDevice *
+new_device_from_type (const char *name, NMDeviceType device_type)
+{
+ GType type;
+ const char *type_desc;
+ NMLinkType link_type = NM_LINK_TYPE_NONE;
+
+ if (nm_manager_get_device (nm_manager_get (), name, device_type))
+ return NULL;
+
+ if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) {
+ type = NM_TYPE_DEVICE_OVS_INTERFACE;
+ type_desc = "OpenVSwitch Interface";
+ link_type = NM_LINK_TYPE_OPENVSWITCH;
+ } else if (device_type == NM_DEVICE_TYPE_OVS_PORT) {
+ type = NM_TYPE_DEVICE_OVS_PORT;
+ type_desc = "OpenVSwitch Port";
+ } else if (device_type == NM_DEVICE_TYPE_OVS_BRIDGE) {
+ type = NM_TYPE_DEVICE_OVS_BRIDGE;
+ type_desc = "OpenVSwitch Bridge";
+ } else {
+ return NULL;
+ }
+
+ return g_object_new (type,
+ NM_DEVICE_IFACE, name,
+ NM_DEVICE_DRIVER, "openvswitch",
+ NM_DEVICE_DEVICE_TYPE, device_type,
+ NM_DEVICE_TYPE_DESC, type_desc,
+ NM_DEVICE_LINK_TYPE, link_type,
+ NULL);
+}
+
+static void
+ovsdb_device_added (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type,
+ NMDeviceFactory *self)
+{
+ NMDevice *device = NULL;
+
+ device = new_device_from_type (name, device_type);
+ if (!device)
+ return;
+
+ g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
+ g_object_unref (device);
+}
+
+static void
+ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type,
+ NMDeviceFactory *self)
+{
+ NMDevice *device;
+ NMDeviceState device_state;
+
+ device = nm_manager_get_device (nm_manager_get (), name, device_type);
+ if (!device)
+ return;
+
+ device_state = nm_device_get_state (device);
+ if ( device_type == NM_DEVICE_TYPE_OVS_INTERFACE
+ && device_state > NM_DEVICE_STATE_DISCONNECTED
+ && device_state < NM_DEVICE_STATE_DEACTIVATING) {
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_DEACTIVATING,
+ NM_DEVICE_STATE_REASON_REMOVED);
+ } else if (device_state == NM_DEVICE_STATE_UNMANAGED) {
+ nm_device_unrealize (device, TRUE, NULL);
+ }
+}
+
+static void
+start (NMDeviceFactory *self)
+{
+ NMOvsdb *ovsdb;
+
+ ovsdb = nm_ovsdb_get ();
+
+ g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_ADDED, G_CALLBACK (ovsdb_device_added), self, (GConnectFlags) 0);
+ g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_REMOVED, G_CALLBACK (ovsdb_device_removed), self, (GConnectFlags) 0);
+}
+
+static NMDevice *
+create_device (NMDeviceFactory *self,
+ const char *iface,
+ const NMPlatformLink *plink,
+ NMConnection *connection,
+ gboolean *out_ignore)
+{
+ NMDeviceType device_type = NM_DEVICE_TYPE_UNKNOWN;
+ const char *connection_type = NULL;
+
+ if (g_strcmp0 (iface, "ovs-system") == 0) {
+ *out_ignore = TRUE;
+ return NULL;
+ }
+
+ if (connection)
+ connection_type = nm_connection_get_connection_type (connection);
+
+ if (plink)
+ device_type = NM_DEVICE_TYPE_OVS_INTERFACE;
+ else if (g_strcmp0 (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0)
+ device_type = NM_DEVICE_TYPE_OVS_INTERFACE;
+ else if (g_strcmp0 (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0)
+ device_type = NM_DEVICE_TYPE_OVS_PORT;
+ else if (g_strcmp0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0)
+ device_type = NM_DEVICE_TYPE_OVS_BRIDGE;
+
+ return new_device_from_type (iface, device_type);
+}
+
+static void
+nm_ovs_factory_init (NMOvsFactory *self)
+{
+}
+
+static void
+nm_ovs_factory_class_init (NMOvsFactoryClass *klass)
+{
+ NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass);
+
+ factory_class->get_supported_types = get_supported_types;
+ factory_class->start = start;
+ factory_class->create_device = create_device;
+}
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c
new file mode 100644
index 0000000000..28f44f287b
--- /dev/null
+++ b/src/devices/ovs/nm-ovsdb.c
@@ -0,0 +1,1591 @@
+/* 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 "devices/nm-device.h"
+#include "platform/nm-platform.h"
+#include "nm-core-internal.h"
+
+/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */
+#ifndef json_boolean
+#define json_boolean(val) ((val) ? json_true() : json_false())
+#endif
+
+/*****************************************************************************/
+
+typedef struct {
+ char *name;
+ char *connection_uuid;
+ GPtrArray *interfaces; /* interface uuids */
+} OpenvswitchPort;
+
+typedef struct {
+ char *name;
+ char *connection_uuid;
+ GPtrArray *ports; /* port uuids */
+} OpenvswitchBridge;
+
+typedef struct {
+ char *name;
+ char *type;
+ char *connection_uuid;
+} OpenvswitchInterface;
+
+/*****************************************************************************/
+
+enum {
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+ DEVICE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+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 => OpenvswitchInterface */
+ GHashTable *ports; /* port uuid => OpenvswitchPort */
+ 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_INTERFACE,
+ OVSDB_DEL_INTERFACE,
+} OvsdbCommand;
+
+typedef struct {
+ gint64 id;
+#define COMMAND_PENDING -1 /* id not yet assigned */
+ OvsdbCommand command;
+ OvsdbMethodCallback callback;
+ gpointer user_data;
+ union {
+ const char *ifname;
+ struct {
+ NMConnection *bridge;
+ NMConnection *port;
+ NMConnection *interface;
+ };
+ };
+} OvsdbMethodCall;
+
+static void
+_call_trace (const char *comment, OvsdbMethodCall *call, json_t *msg)
+{
+#ifdef NM_MORE_LOGGING
+ char *str = NULL;
+
+ if (msg)
+ str = json_dumps (msg, 0);
+
+ switch (call->command) {
+ case OVSDB_MONITOR:
+ _LOGT ("%s: monitor%s%s",
+ comment,
+ msg ? ": " : "",
+ msg ? str : "");
+ break;
+ case OVSDB_ADD_INTERFACE:
+ _LOGT ("%s: add-iface bridge=%s port=%s interface=%s%s%s",
+ comment,
+ nm_connection_get_interface_name (call->bridge),
+ nm_connection_get_interface_name (call->port),
+ nm_connection_get_interface_name (call->interface),
+ msg ? ": " : "",
+ msg ? str : "");
+ break;
+ case OVSDB_DEL_INTERFACE:
+ _LOGT ("%s: del-iface interface=%s%s%s",
+ comment, call->ifname,
+ msg ? ": " : "",
+ msg ? str : "");
+ break;
+ }
+
+ if (msg)
+ g_free (str);
+#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 *ifname,
+ NMConnection *bridge, NMConnection *port, NMConnection *interface,
+ OvsdbMethodCallback callback, gpointer user_data)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_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 = COMMAND_PENDING;
+ call->command = command;
+ call->callback = callback;
+ call->user_data = user_data;
+
+ switch (call->command) {
+ case OVSDB_MONITOR:
+ break;
+ case OVSDB_ADD_INTERFACE:
+ call->bridge = nm_simple_connection_new_clone (bridge);
+ call->port = nm_simple_connection_new_clone (port);
+ call->interface = nm_simple_connection_new_clone (interface);
+ break;
+ case OVSDB_DEL_INTERFACE:
+ call->ifname = g_strdup (ifname);
+ break;
+ }
+
+ _call_trace ("enqueue", call, NULL);
+
+ ovsdb_next_command (self);
+}
+
+/*****************************************************************************/
+
+/* Create and process the JSON-RPC messages from ovsdb. */
+
+/**
+ * _expect_ovs_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 void
+_expect_ovs_bridges (json_t *params, const char *db_uuid, json_t *bridges)
+{
+ json_array_append_new (params,
+ 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_ovs_bridges:
+ *
+ * Return a command that will update the list of bridges in @db_uuid
+ * database to @new_bridges.
+ */
+static void
+_set_ovs_bridges (json_t *params, const char *db_uuid, json_t *new_bridges)
+{
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, [s, s]]]}",
+ "op", "update", "table", "Open_vSwitch",
+ "row", "bridges", "set", new_bridges,
+ "where", "_uuid", "==", "uuid", db_uuid)
+ );
+}
+
+/**
+ * _expect_bridge_ports:
+ *
+ * Return a command that will fail the transaction if the actual set of
+ * ports in bridge @ifname 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 void
+_expect_bridge_ports (json_t *params, const char *ifname, json_t *ports)
+{
+ json_array_append_new (params,
+ 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", "==", ifname)
+ );
+}
+
+/**
+ * _set_bridge_ports:
+ *
+ * Return a command that will update the list of ports of bridge
+ * @ifname to @new_ports.
+ */
+static void
+_set_bridge_ports (json_t *params, const char *ifname, json_t *new_ports)
+{
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, s]]}",
+ "op", "update", "table", "Bridge",
+ "row", "ports", "set", new_ports,
+ "where", "name", "==", ifname)
+ );
+}
+
+/**
+ * _expect_port_interfaces:
+ *
+ * Return a command that will fail the transaction if the actual set of
+ * interfaces in port @ifname 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 void
+_expect_port_interfaces (json_t *params, const char *ifname, json_t *interfaces)
+{
+ json_array_append_new (params,
+ 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", "==", ifname)
+ );
+}
+
+/**
+ * _set_port_interfaces:
+ *
+ * Return a command that will update the list of interfaces of port @ifname
+ * to @new_interfaces.
+ */
+static void
+_set_port_interfaces (json_t *params, const char *ifname, json_t *new_interfaces)
+{
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, s]]}",
+ "op", "update", "table", "Port",
+ "row", "interfaces", "set", new_interfaces,
+ "where", "name", "==", ifname)
+ );
+}
+
+/**
+ * _insert_interface:
+ *
+ * Returns an commands that adds new interface from a given connection.
+ */
+static void
+_insert_interface (json_t *params, NMConnection *interface)
+{
+ const char *type = NULL;
+ NMSettingOvsInterface *s_ovs_iface;
+ NMSettingOvsPatch *s_ovs_patch;
+ json_t *options = json_array ();
+
+ s_ovs_iface = nm_connection_get_setting_ovs_interface (interface);
+ if (s_ovs_iface)
+ type = nm_setting_ovs_interface_get_interface_type (s_ovs_iface);
+
+ json_array_append (options, json_string ("map"));
+ s_ovs_patch = nm_connection_get_setting_ovs_patch (interface);
+ if (s_ovs_patch) {
+ json_array_append (options, json_pack ("[[s, s]]",
+ "peer",
+ nm_setting_ovs_patch_get_peer (s_ovs_patch)));
+ } else {
+ json_array_append (options, json_array ());
+ }
+
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:{s:s, s:s, s:o, s:[s, [[s, s]]]}, s:s}",
+ "op", "insert", "table", "Interface", "row",
+ "name", nm_connection_get_interface_name (interface),
+ "type", type ? type : "",
+ "options", options,
+ "external_ids", "map", "NM.connection.uuid", nm_connection_get_uuid (interface),
+ "uuid-name", "rowInterface"));
+}
+
+/**
+ * _insert_port:
+ *
+ * Returns an commands that adds new port from a given connection.
+ */
+static void
+_insert_port (json_t *params, NMConnection *port, json_t *new_interfaces)
+{
+ NMSettingOvsPort *s_ovs_port;
+ const char *vlan_mode = NULL;
+ guint tag = 0;
+ const char *lacp = NULL;
+ const char *bond_mode = NULL;
+ guint bond_updelay = 0;
+ guint bond_downdelay = 0;
+ json_t *row;
+
+ s_ovs_port = nm_connection_get_setting_ovs_port (port);
+
+ row = json_object ();
+
+ if (s_ovs_port) {
+ vlan_mode = nm_setting_ovs_port_get_vlan_mode (s_ovs_port);
+ tag = nm_setting_ovs_port_get_tag (s_ovs_port);
+ lacp = nm_setting_ovs_port_get_lacp (s_ovs_port);
+ bond_mode = nm_setting_ovs_port_get_bond_mode (s_ovs_port);
+ bond_updelay = nm_setting_ovs_port_get_bond_updelay (s_ovs_port);
+ bond_downdelay = nm_setting_ovs_port_get_bond_downdelay (s_ovs_port);
+ }
+
+ if (vlan_mode)
+ json_object_set_new (row, "vlan_mode", json_string (vlan_mode));
+ if (tag)
+ json_object_set_new (row, "tag", json_integer (tag));
+ if (lacp)
+ json_object_set_new (row, "lacp", json_string (lacp));
+ if (bond_mode)
+ json_object_set_new (row, "bond_mode", json_string (bond_mode));
+ if (bond_updelay)
+ json_object_set_new (row, "bond_updelay", json_integer (bond_updelay));
+ if (bond_downdelay)
+ json_object_set_new (row, "bond_downdelay", json_integer (bond_downdelay));
+
+ json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (port)));
+ json_object_set_new (row, "interfaces", json_pack ("[s, O]", "set", new_interfaces));
+ json_object_set_new (row, "external_ids",
+ json_pack ("[s, [[s, s]]]", "map",
+ "NM.connection.uuid", nm_connection_get_uuid (port)));
+
+ /* Create a new one. */
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Port",
+ "row", row, "uuid-name", "rowPort"));
+}
+
+/**
+ * _insert_bridge:
+ *
+ * Returns an commands that adds new bridge from a given connection.
+ */
+static void
+_insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports)
+{
+ NMSettingOvsBridge *s_ovs_bridge;
+ const char *fail_mode = NULL;
+ gboolean mcast_snooping_enable = FALSE;
+ gboolean rstp_enable = FALSE;
+ gboolean stp_enable = FALSE;
+ json_t *row;
+
+ s_ovs_bridge = nm_connection_get_setting_ovs_bridge (bridge);
+
+ row = json_object ();
+
+ if (s_ovs_bridge) {
+ fail_mode = nm_setting_ovs_bridge_get_fail_mode (s_ovs_bridge);
+ mcast_snooping_enable = nm_setting_ovs_bridge_get_mcast_snooping_enable (s_ovs_bridge);
+ rstp_enable = nm_setting_ovs_bridge_get_rstp_enable (s_ovs_bridge);
+ stp_enable = nm_setting_ovs_bridge_get_stp_enable (s_ovs_bridge);
+ }
+
+ if (fail_mode)
+ json_object_set_new (row, "fail_mode", json_string (fail_mode));
+ if (mcast_snooping_enable)
+ json_object_set_new (row, "mcast_snooping_enable", json_boolean (mcast_snooping_enable));
+ if (rstp_enable)
+ json_object_set_new (row, "rstp_enable", json_boolean (rstp_enable));
+ if (stp_enable)
+ json_object_set_new (row, "stp_enable", json_boolean (stp_enable));
+
+ json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (bridge)));
+ json_object_set_new (row, "ports", json_pack ("[s, O]", "set", new_ports));
+ json_object_set_new (row, "external_ids",
+ json_pack ("[s, [[s, s]]]", "map",
+ "NM.connection.uuid", nm_connection_get_uuid (bridge)));
+
+ /* Create a new one. */
+ json_array_append_new (params,
+ json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Bridge",
+ "row", row, "uuid-name", "rowBridge"));
+}
+
+/**
+ * _inc_next_cfg:
+ *
+ * Returns an mutate command 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);
+}
+
+/**
+ * _add_interface:
+ *
+ * Adds an interface as specified by @interface connection, optionally creating
+ * a parent @port and @bridge if needed.
+ */
+static void
+_add_interface (NMOvsdb *self, json_t *params,
+ NMConnection *bridge, NMConnection *port, NMConnection *interface)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GHashTableIter iter;
+ const char *bridge_uuid;
+ const char *port_uuid;
+ const char *interface_uuid;
+ OpenvswitchBridge *ovs_bridge = NULL;
+ OpenvswitchPort *ovs_port = NULL;
+ OpenvswitchInterface *ovs_interface = NULL;
+ int pi;
+ int ii;
+ json_t *bridges, *new_bridges;
+ json_t *ports, *new_ports;
+ json_t *interfaces, *new_interfaces;
+ gboolean has_interface = FALSE;
+
+ bridges = json_array ();
+ ports = json_array ();
+ interfaces = json_array ();
+ new_bridges = json_array ();
+ new_ports = json_array ();
+ new_interfaces = 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 (bridges, json_pack ("[s, s]", "uuid", bridge_uuid));
+
+ if ( g_strcmp0 (ovs_bridge->name, nm_connection_get_interface_name (bridge)) != 0
+ || g_strcmp0 (ovs_bridge->connection_uuid, nm_connection_get_uuid (bridge)) != 0)
+ continue;
+
+ for (pi = 0; pi < ovs_bridge->ports->len; pi++) {
+ port_uuid = g_ptr_array_index (ovs_bridge->ports, pi);
+ ovs_port = g_hash_table_lookup (priv->ports, port_uuid);
+
+ json_array_append_new (ports, json_pack ("[s, s]", "uuid", port_uuid));
+
+ if ( g_strcmp0 (ovs_port->name, nm_connection_get_interface_name (port)) != 0
+ || g_strcmp0 (ovs_port->connection_uuid, nm_connection_get_uuid (port)) != 0)
+ continue;
+
+ for (ii = 0; ii < ovs_port->interfaces->len; ii++) {
+ interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii);
+ ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid);
+
+ json_array_append_new (interfaces, json_pack ("[s, s]", "uuid", interface_uuid));
+
+ if ( g_strcmp0 (ovs_interface->name, nm_connection_get_interface_name (interface)) == 0
+ && g_strcmp0 (ovs_interface->connection_uuid, nm_connection_get_uuid (interface)) == 0)
+ has_interface = TRUE;
+ }
+
+ break;
+ }
+
+ break;
+ }
+
+ json_array_extend (new_bridges, bridges);
+ json_array_extend (new_ports, ports);
+ json_array_extend (new_interfaces, interfaces);
+
+ if (json_array_size (interfaces) == 0) {
+ /* Need to create a port. */
+ if (json_array_size (ports) == 0) {
+ /* Need to create a bridge. */
+ _expect_ovs_bridges (params, priv->db_uuid, bridges);
+ json_array_append_new (new_bridges, json_pack ("[s, s]", "named-uuid", "rowBridge"));
+ _set_ovs_bridges (params, priv->db_uuid, new_bridges);
+ _insert_bridge (params, bridge, new_ports);
+ } else {
+ /* Bridge already exists. */
+ g_return_if_fail (ovs_bridge);
+ _expect_bridge_ports (params, ovs_bridge->name, ports);
+ _set_bridge_ports (params, nm_connection_get_interface_name (bridge), new_ports);
+ }
+
+ json_array_append_new (new_ports, json_pack ("[s, s]", "named-uuid", "rowPort"));
+ _insert_port (params, port, new_interfaces);
+ } else {
+ /* Port already exists */
+ g_return_if_fail (ovs_port);
+ _expect_port_interfaces (params, ovs_port->name, interfaces);
+ _set_port_interfaces (params, nm_connection_get_interface_name (port), new_interfaces);
+ }
+
+ if (!has_interface) {
+ _insert_interface (params, interface);
+ json_array_append_new (new_interfaces, json_pack ("[s, s]", "named-uuid", "rowInterface"));
+ }
+
+ json_decref (interfaces);
+ json_decref (ports);
+ json_decref (bridges);
+
+ json_decref (new_interfaces);
+ json_decref (new_ports);
+ json_decref (new_bridges);
+}
+
+/**
+ * _delete_interface:
+ *
+ * Removes an interface of @ifname name, collecting empty ports and bridge
+ * if last item is removed from them.
+ */
+static void
+_delete_interface (NMOvsdb *self, json_t *params, const char *ifname)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+ GHashTableIter iter;
+ char *bridge_uuid;
+ char *port_uuid;
+ char *interface_uuid;
+ OpenvswitchBridge *ovs_bridge;
+ OpenvswitchPort *ovs_port;
+ OpenvswitchInterface *ovs_interface;
+ int pi;
+ int ii;
+ json_t *bridges, *new_bridges;
+ json_t *ports, *new_ports;
+ json_t *interfaces, *new_interfaces;
+ gboolean bridges_changed;
+ gboolean ports_changed;
+ gboolean interfaces_changed;
+
+ bridges = json_array ();
+ new_bridges = json_array ();
+ bridges_changed = FALSE;
+
+ 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 (bridges, json_pack ("[s,s]", "uuid", bridge_uuid));
+
+ ports = json_array ();
+ new_ports = json_array ();
+ ports_changed = FALSE;
+
+ for (pi = 0; pi < ovs_bridge->ports->len; pi++) {
+ port_uuid = g_ptr_array_index (ovs_bridge->ports, pi);
+ ovs_port = g_hash_table_lookup (priv->ports, port_uuid);
+
+ json_array_append_new (ports, json_pack ("[s,s]", "uuid", port_uuid));
+
+ interfaces = json_array ();
+ new_interfaces = json_array ();
+ interfaces_changed = FALSE;
+
+ for (ii = 0; ii < ovs_port->interfaces->len; ii++) {
+ interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii);
+ ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid);
+
+ json_array_append_new (interfaces, json_pack ("[s,s]", "uuid", interface_uuid));
+
+ if (strcmp (ovs_interface->name, ifname) == 0) {
+ /* skip the interface */
+ interfaces_changed = TRUE;
+ continue;
+ }
+
+ json_array_append_new (new_interfaces, json_pack ("[s,s]", "uuid", interface_uuid));
+ }
+
+ if (json_array_size (new_interfaces) == 0) {
+ ports_changed = TRUE;
+ } else {
+ if (interfaces_changed) {
+ _expect_port_interfaces (params, ovs_port->name, interfaces);
+ _set_port_interfaces (params, ovs_port->name, new_interfaces);
+ }
+ json_array_append_new (new_ports, json_pack ("[s,s]", "uuid", port_uuid));
+ }
+
+ json_decref (interfaces);
+ json_decref (new_interfaces);
+ }
+
+ if (json_array_size (new_ports) == 0) {
+ bridges_changed = TRUE;
+ } else {
+ if (ports_changed) {
+ _expect_bridge_ports (params, ovs_bridge->name, ports);
+ _set_bridge_ports (params, ovs_bridge->name, new_ports);
+ }
+ json_array_append_new (new_bridges, json_pack ("[s,s]", "uuid", bridge_uuid));
+ }
+
+ json_decref (ports);
+ json_decref (new_ports);
+ }
+
+ if (bridges_changed) {
+ _expect_ovs_bridges (params, priv->db_uuid, bridges);
+ _set_ovs_bridges (params, priv->db_uuid, new_bridges);
+ }
+}
+
+/**
+ * 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 *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, s, s]}],"
+ " s:[{s:[]}]"
+ "}]}",
+ "id", call->id,
+ "method", "monitor", "params", "Open_vSwitch",
+ "Bridge", "columns", "name", "ports", "external_ids",
+ "Port", "columns", "name", "interfaces", "external_ids",
+ "Interface", "columns", "name", "type", "external_ids",
+ "Open_vSwitch", "columns");
+ break;
+ case OVSDB_ADD_INTERFACE:
+ params = json_array ();
+ json_array_append_new (params, json_string ("Open_vSwitch"));
+ json_array_append_new (params, _inc_next_cfg (priv->db_uuid));
+
+ _add_interface (self, params, call->bridge, call->port, call->interface);
+
+ msg = json_pack ("{s:i, s:s, s:o}",
+ "id", call->id,
+ "method", "transact", "params", params);
+ break;
+ case OVSDB_DEL_INTERFACE:
+ params = json_array ();
+ json_array_append_new (params, json_string ("Open_vSwitch"));
+ json_array_append_new (params, _inc_next_cfg (priv->db_uuid));
+
+ _delete_interface (self, params, call->ifname);
+
+ msg = json_pack ("{s:i, s:s, s:o}",
+ "id", call->id,
+ "method", "transact", "params", params);
+ break;
+ }
+
+ g_return_if_fail (msg);
+ _call_trace ("send", call, 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);
+ }
+ }
+ }
+}
+
+static char *
+_connection_uuid_from_external_ids (json_t *external_ids)
+{
+ json_t *value;
+ size_t index;
+
+ if (g_strcmp0 ("map", json_string_value (json_array_get (external_ids, 0))) != 0)
+ return NULL;
+
+ json_array_foreach (json_array_get (external_ids, 1), index, value) {
+ if (g_strcmp0 ("NM.connection.uuid", json_string_value (json_array_get (value, 0))) == 0)
+ return g_strdup (json_string_value (json_array_get (value, 1)));
+ }
+
+ return NULL;
+}
+
+/**
+ * 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_t *external_ids;
+ json_error_t json_error = { 0, };
+ void *iter;
+ const char *name;
+ const char *key;
+ const char *type;
+ json_t *value;
+ OpenvswitchBridge *ovs_bridge;
+ OpenvswitchPort *ovs_port;
+ OpenvswitchInterface *ovs_interface;
+
+ 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);
+ }
+
+ /* Interfaces */
+ json_object_foreach (interface, key, value) {
+ gboolean old = FALSE;
+ gboolean new = FALSE;
+
+ if (json_unpack (value, "{s:{}}", "old") == 0)
+ old = TRUE;
+
+ if (json_unpack (value, "{s:{s:s, s:s, s:o}}", "new",
+ "name", &name,
+ "type", &type,
+ "external_ids", &external_ids) == 0)
+ new = TRUE;
+
+ if (old) {
+ ovs_interface = g_hash_table_lookup (priv->interfaces, key);
+ if (!new || g_strcmp0 (ovs_interface->name, name) != 0) {
+ old = FALSE;
+ _LOGT ("removed an '%s' interface: %s%s%s",
+ ovs_interface->type, ovs_interface->name,
+ ovs_interface->connection_uuid ? ", " : "",
+ ovs_interface->connection_uuid ? ovs_interface->connection_uuid : "");
+ if (g_strcmp0 (ovs_interface->type, "internal") == 0) {
+ /* Currently the factory only creates NMDevices for
+ * internal interfaces. Ignore the rest. */
+ g_signal_emit (self, signals[DEVICE_REMOVED], 0,
+ ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE);
+ }
+ }
+ g_hash_table_remove (priv->interfaces, key);
+ }
+
+ if (new) {
+ ovs_interface = g_slice_new (OpenvswitchInterface);
+ ovs_interface->name = g_strdup (name);
+ ovs_interface->type = g_strdup (type);
+ ovs_interface->connection_uuid = _connection_uuid_from_external_ids (external_ids);
+ if (old) {
+ _LOGT ("changed an '%s' interface: %s%s%s", type, ovs_interface->name,
+ ovs_interface->connection_uuid ? ", " : "",
+ ovs_interface->connection_uuid ? ovs_interface->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_CHANGED], 0,
+ "ovs-interface", ovs_interface->name);
+ } else {
+ _LOGT ("added an '%s' interface: %s%s%s",
+ ovs_interface->type, ovs_interface->name,
+ ovs_interface->connection_uuid ? ", " : "",
+ ovs_interface->connection_uuid ? ovs_interface->connection_uuid : "");
+ if (g_strcmp0 (ovs_interface->type, "internal") == 0) {
+ /* Currently the factory only creates NMDevices for
+ * internal interfaces. Ignore the rest. */
+ g_signal_emit (self, signals[DEVICE_ADDED], 0,
+ ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE);
+ }
+ }
+ g_hash_table_insert (priv->interfaces, g_strdup (key), ovs_interface);
+ }
+ }
+
+ /* Ports */
+ json_object_foreach (port, key, value) {
+ gboolean old = FALSE;
+ gboolean new = FALSE;
+
+ if (json_unpack (value, "{s:{}}", "old") == 0)
+ old = TRUE;
+
+ if (json_unpack (value, "{s:{s:s, s:o, s:o}}", "new",
+ "name", &name,
+ "external_ids", &external_ids,
+ "interfaces", &items) == 0)
+ new = TRUE;
+
+ if (old) {
+ ovs_port = g_hash_table_lookup (priv->ports, key);
+ if (!new || g_strcmp0 (ovs_port->name, name) != 0) {
+ old = FALSE;
+ _LOGT ("removed a port: %s%s%s", ovs_port->name,
+ ovs_port->connection_uuid ? ", " : "",
+ ovs_port->connection_uuid ? ovs_port->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_REMOVED], 0,
+ ovs_port->name, NM_DEVICE_TYPE_OVS_PORT);
+ }
+ g_hash_table_remove (priv->ports, key);
+ }
+
+ if (new) {
+ ovs_port = g_slice_new (OpenvswitchPort);
+ ovs_port->name = g_strdup (name);
+ ovs_port->connection_uuid = _connection_uuid_from_external_ids (external_ids);
+ ovs_port->interfaces = g_ptr_array_new_with_free_func (g_free);
+ _uuids_to_array (ovs_port->interfaces, items);
+ if (old) {
+ _LOGT ("changed a port: %s%s%s", ovs_port->name,
+ ovs_port->connection_uuid ? ", " : "",
+ ovs_port->connection_uuid ? ovs_port->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_CHANGED], 0,
+ NM_SETTING_OVS_PORT_SETTING_NAME, ovs_port->name);
+ } else {
+ _LOGT ("added a port: %s%s%s", ovs_port->name,
+ ovs_port->connection_uuid ? ", " : "",
+ ovs_port->connection_uuid ? ovs_port->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_ADDED], 0,
+ ovs_port->name, NM_DEVICE_TYPE_OVS_PORT);
+ }
+ g_hash_table_insert (priv->ports, g_strdup (key), ovs_port);
+ }
+ }
+
+ /* Bridges */
+ json_object_foreach (bridge, key, value) {
+ gboolean old = FALSE;
+ gboolean new = FALSE;
+
+ if (json_unpack (value, "{s:{}}", "old") == 0)
+ old = TRUE;
+
+ if (json_unpack (value, "{s:{s:s, s:o, s:o}}", "new",
+ "name", &name,
+ "external_ids", &external_ids,
+ "ports", &items) == 0)
+ new = TRUE;
+
+ if (old) {
+ ovs_bridge = g_hash_table_lookup (priv->bridges, key);
+ if (!new || g_strcmp0 (ovs_bridge->name, name) != 0) {
+ old = FALSE;
+ _LOGT ("removed a bridge: %s%s%s", ovs_bridge->name,
+ ovs_bridge->connection_uuid ? ", " : "",
+ ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_REMOVED], 0,
+ ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE);
+ }
+ g_hash_table_remove (priv->bridges, key);
+ }
+
+ if (new) {
+ ovs_bridge = g_slice_new (OpenvswitchBridge);
+ ovs_bridge->name = g_strdup (name);
+ ovs_bridge->connection_uuid = _connection_uuid_from_external_ids (external_ids);
+ ovs_bridge->ports = g_ptr_array_new_with_free_func (g_free);
+ _uuids_to_array (ovs_bridge->ports, items);
+ if (old) {
+ _LOGT ("changed a bridge: %s%s%s", ovs_bridge->name,
+ ovs_bridge->connection_uuid ? ", " : "",
+ ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_CHANGED], 0,
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME, ovs_bridge->name);
+ } else {
+ _LOGT ("added a bridge: %s%s%s", ovs_bridge->name,
+ ovs_bridge->connection_uuid ? ", " : "",
+ ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : "");
+ g_signal_emit (self, signals[DEVICE_ADDED], 0,
+ ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE);
+ }
+ g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge);
+ }
+ }
+
+}
+
+/**
+ * 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, msg);
+
+ 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, 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);
+}
+
+void
+nm_ovsdb_add_interface (NMOvsdb *self,
+ NMConnection *bridge, NMConnection *port, NMConnection *interface,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ OvsdbCall *call;
+
+ call = g_slice_new (OvsdbCall);
+ call->callback = callback;
+ call->user_data = user_data;
+
+ ovsdb_call_method (self, OVSDB_ADD_INTERFACE, NULL,
+ bridge, port, interface, _transact_cb, call);
+}
+
+void
+nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname,
+ NMOvsdbCallback callback, gpointer user_data)
+{
+ OvsdbCall *call;
+
+ call = g_slice_new (OvsdbCall);
+ call->callback = callback;
+ call->user_data = user_data;
+
+ ovsdb_call_method (self, OVSDB_DEL_INTERFACE, ifname,
+ NULL, NULL, NULL, _transact_cb, call);
+}
+
+/*****************************************************************************/
+
+static void
+_clear_call (gpointer data)
+{
+ OvsdbMethodCall *call = data;
+
+ switch (call->command) {
+ case OVSDB_MONITOR:
+ break;
+ case OVSDB_ADD_INTERFACE:
+ g_clear_object (&call->bridge);
+ g_clear_object (&call->port);
+ g_clear_object (&call->interface);
+ break;
+ case OVSDB_DEL_INTERFACE:
+ g_clear_pointer (&call->ifname, g_free);
+ break;
+ }
+}
+
+static void
+_free_bridge (gpointer data)
+{
+ OpenvswitchBridge *ovs_bridge = data;
+
+ g_free (ovs_bridge->name);
+ g_free (ovs_bridge->connection_uuid);
+ 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_free (ovs_port->name);
+ g_free (ovs_port->connection_uuid);
+ g_ptr_array_free (ovs_port->interfaces, TRUE);
+ g_slice_free (OpenvswitchPort, ovs_port);
+}
+
+static void
+_free_interface (gpointer data)
+{
+ OpenvswitchInterface *ovs_interface = data;
+
+ g_free (ovs_interface->name);
+ g_free (ovs_interface->connection_uuid);
+ g_free (ovs_interface->type);
+ g_slice_free (OpenvswitchInterface, ovs_interface);
+}
+
+static void
+nm_ovsdb_init (NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
+
+ priv->calls = g_array_new (FALSE, TRUE, sizeof (OvsdbMethodCall));
+ g_array_set_clear_func (priv->calls, _clear_call);
+ priv->input = g_string_new (NULL);
+ priv->output = g_string_new (NULL);
+ priv->bridges = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_bridge);
+ priv->ports = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_port);
+ priv->interfaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_interface);
+
+ 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;
+
+ signals[DEVICE_ADDED] =
+ g_signal_new (NM_OVSDB_DEVICE_ADDED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
+
+ signals[DEVICE_REMOVED] =
+ g_signal_new (NM_OVSDB_DEVICE_REMOVED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
+
+ signals[DEVICE_CHANGED] =
+ g_signal_new (NM_OVSDB_DEVICE_CHANGED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
+}
diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h
new file mode 100644
index 0000000000..cf9fe2a21b
--- /dev/null
+++ b/src/devices/ovs/nm-ovsdb.h
@@ -0,0 +1,50 @@
+/* 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))
+
+#define NM_OVSDB_DEVICE_ADDED "device-added"
+#define NM_OVSDB_DEVICE_REMOVED "device-removed"
+#define NM_OVSDB_DEVICE_CHANGED "device-changed"
+
+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_interface (NMOvsdb *self,
+ NMConnection *bridge, NMConnection *port, NMConnection *interface,
+ NMOvsdbCallback callback, gpointer user_data);
+
+void nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname,
+ NMOvsdbCallback callback, gpointer user_data);
+
+#endif /* __NETWORKMANAGER_OVSDB_H__ */
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 46214130b3..be64a868ba 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1270,22 +1270,38 @@ nm_manager_iface_for_uuid (NMManager *self, const char *uuid)
return nm_connection_get_interface_name (NM_CONNECTION (connection));
}
-gboolean
-nm_manager_remove_device (NMManager *self, const char *ifname)
+NMDevice *
+nm_manager_get_device (NMManager *self, const char *ifname, NMDeviceType device_type)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GSList *iter;
NMDevice *d;
+ g_return_val_if_fail (ifname, NULL);
+ g_return_val_if_fail (device_type != NM_DEVICE_TYPE_UNKNOWN, NULL);
+
for (iter = priv->devices; iter; iter = iter->next) {
d = iter->data;
- if (nm_streq0 (nm_device_get_iface (d), ifname)) {
- remove_device (self, d, FALSE, FALSE);
- return TRUE;
- }
+
+ if ( nm_device_get_device_type (d) == device_type
+ && nm_streq0 (nm_device_get_iface (d), ifname))
+ return d;
}
- return FALSE;
+ return NULL;
+}
+
+gboolean
+nm_manager_remove_device (NMManager *self, const char *ifname, NMDeviceType device_type)
+{
+ NMDevice *d;
+
+ d = nm_manager_get_device (self, ifname, device_type);
+ if (!d)
+ return FALSE;
+
+ remove_device (self, d, FALSE, FALSE);
+ return TRUE;
}
/**
@@ -2031,6 +2047,7 @@ device_ip_iface_changed (NMDevice *device,
NMManager *self)
{
const char *ip_iface = nm_device_get_ip_iface (device);
+ NMDeviceType device_type = nm_device_get_device_type (device);
GSList *iter;
/* Remove NMDevice objects that are actually child devices of others,
@@ -2043,6 +2060,7 @@ device_ip_iface_changed (NMDevice *device,
if ( candidate != device
&& g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0
+ && nm_device_get_device_type (candidate) == device_type
&& nm_device_is_real (candidate)) {
remove_device (self, candidate, FALSE, FALSE);
break;
@@ -2339,6 +2357,9 @@ platform_link_added (NMManager *self,
gboolean compatible = TRUE;
gs_free_error GError *error = NULL;
+ if (nm_device_get_link_type (candidate) != plink->type)
+ continue;
+
if (strcmp (nm_device_get_iface (candidate), plink->name))
continue;
@@ -2346,6 +2367,7 @@ platform_link_added (NMManager *self,
/* Ignore the link added event since there's already a realized
* device with the link's name.
*/
+ nm_device_update_from_platform_link (candidate, plink);
return;
} else if (nm_device_realize_start (candidate,
plink,
@@ -2476,6 +2498,8 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
_LOG2W (LOGD_DEVICE, device, "failed to unrealize: %s", error->message);
g_clear_error (&error);
remove_device (self, device, FALSE, TRUE);
+ } else {
+ nm_device_update_from_platform_link (device, NULL);
}
} else {
/* Hardware and external devices always get removed when their kernel link is gone */
diff --git a/src/nm-manager.h b/src/nm-manager.h
index fdcae121c5..622edb5b54 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -126,6 +126,11 @@ gboolean nm_manager_deactivate_connection (NMManager *manager,
void nm_manager_set_capability (NMManager *self, NMCapability cap);
-gboolean nm_manager_remove_device (NMManager *self, const char *ifname);
+NMDevice * nm_manager_get_device (NMManager *self,
+ const char *ifname,
+ NMDeviceType device_type);
+gboolean nm_manager_remove_device (NMManager *self,
+ const char *ifname,
+ NMDeviceType device_type);
#endif /* __NETWORKMANAGER_MANAGER_H__ */