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