Thomas Haller 2020-11-17 19:49:43 +01:00
commit 7595a8f817
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
12 changed files with 819 additions and 282 deletions

View file

@ -12,25 +12,68 @@ import sys
import os
import re
import pprint
import subprocess
import gi
gi.require_version("NM", "1.0")
from gi.repository import GLib, NM
###############################################################################
MODE_GET = "get"
MODE_SET = "set"
MODE_APPLY = "apply"
def memoize0(f):
result = []
def helper():
if len(result) == 0:
result.append(f())
return result[0]
return helper
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
def pr(v):
pprint.pprint(v, indent=4, depth=5, width=60)
HAS_LIBNM_DEBUG = os.getenv("LIBNM_CLIENT_DEBUG") is not None
@memoize0
def is_libnm_debug():
return os.getenv("LIBNM_CLIENT_DEBUG") is not None
@memoize0
def can_sudo():
try:
return (
subprocess.run(
["sudo", "-n", "true"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
).returncode
== 0
)
except:
return False
def _print(msg=""):
if HAS_LIBNM_DEBUG:
if is_libnm_debug():
# we want to use the same logging mechanism as libnm's debug
# logging with "LIBNM_CLIENT_DEBUG=trace,stdout".
NM.utils_print(0, msg + "\n")
@ -63,12 +106,106 @@ def mainloop_run(timeout_msec=0, mainloop=None):
return not timeout_reached
###############################################################################
def connection_update2(remote_connection, connection):
mainloop = GLib.MainLoop()
result_error = []
def cb(c, result):
try:
c.update2_finish(result)
except Exception as e:
result_error.append(e)
mainloop.quit()
remote_connection.update2(
connection.to_dbus(NM.ConnectionSerializationFlags.ALL),
NM.SettingsUpdate2Flags.NO_REAPPLY,
None,
None,
cb,
)
mainloop_run(mainloop=mainloop)
if result_error:
raise result_error[0]
def device_get_applied_connection(device):
mainloop = GLib.MainLoop()
rr = []
def cb(c, result):
try:
con, version_id = c.get_applied_connection_finish(result)
except Exception as e:
rr.append(e)
else:
rr.append(con)
rr.append(version_id)
mainloop.quit()
device.get_applied_connection_async(0, None, cb)
mainloop_run(mainloop=mainloop)
if len(rr) == 1:
raise rr[0]
return rr[0], rr[1]
def device_reapply(device, connection, version_id):
mainloop = GLib.MainLoop()
result_error = []
def cb(d, result):
try:
d.reapply_finish(result)
except Exception as e:
result_error.append(e)
mainloop.quit()
device.reapply_async(connection, version_id, 0, None, cb)
mainloop_run(mainloop=mainloop)
if len(result_error) == 1:
raise result_error[0]
def ovs_print_external_ids(prefix):
if not can_sudo():
_print(prefix + ": not running as root and cannot call ovs-vsctl")
return
cmds = [["ovs-vsctl", "show"]]
for typ in ["Bridge", "Port", "Interface"]:
cmds += [["ovs-vsctl", "--columns=name,external-ids", "list", typ]]
out = ""
for cmd in cmds:
p = subprocess.run(cmd, stdout=subprocess.PIPE, check=True,)
out += p.stdout.decode("utf-8") + "\n"
out = "\n".join([prefix + s for s in out.split("\n")])
_print(out)
###############################################################################
def usage():
_print("%s g[et] PROFILE [ GETTER ]" % (sys.argv[0]))
_print("%s s[et] [--test] PROFILE SETTER" % (sys.argv[0]))
_print("%s g[et] PROFILE [ GETTER ]" % (sys.argv[0]))
_print("%s s[et] PROFILE SETTER [--test]" % (sys.argv[0]))
_print("%s a[pply] DEVICE SETTER [--test]" % (sys.argv[0]))
_print(
" PROFILE := [id | uuid | type] STRING | [ ~id | ~type ] REGEX_STRING | STRING"
)
_print(" DEVICE := [iface] STRING")
_print(" GETTER := ( KEY | ~REGEX_KEY ) [... GETTER]")
_print(" SETTER := ( + | - | -KEY | [+]KEY VALUE ) [... SETTER]")
@ -88,7 +225,7 @@ def parse_args(argv):
had_dash_dash = False
args = {
"mode": MODE_GET,
"profile_arg": None,
"select_arg": None,
"ids_arg": [],
"do_test": False,
}
@ -101,6 +238,8 @@ def parse_args(argv):
args["mode"] = MODE_SET
elif a in ["g", "get"]:
args["mode"] = MODE_GET
elif a in ["a", "apply"]:
args["mode"] = MODE_APPLY
else:
die_usage("unexpected mode argument '%s'" % (a))
i += 1
@ -111,17 +250,22 @@ def parse_args(argv):
i += 1
continue
if args["profile_arg"] is None:
if a in ["id", "~id", "uuid", "type", "~type"]:
if args["select_arg"] is None:
if args["mode"] == MODE_APPLY:
possible_selects = ["iface"]
else:
possible_selects = ["id", "~id", "uuid", "type", "~type"]
if a in possible_selects:
if i + 1 >= len(argv):
die_usage("'%s' requires an argument'" % (a))
args["profile_arg"] = (a, argv[i + 1])
args["select_arg"] = (a, argv[i + 1])
i += 2
continue
if a == "*":
a = None
args["profile_arg"] = ("*", a)
args["select_arg"] = ("*", a)
i += 1
continue
@ -156,6 +300,12 @@ def parse_args(argv):
return args
def device_to_str(device, show_type=False):
if show_type:
return "%s (%s)" % (device.get_iface(), device.get_type_desc())
return "%s" % (device.get_iface(),)
def connection_to_str(connection, show_type=False):
if show_type:
return "%s (%s, %s)" % (
@ -166,15 +316,38 @@ def connection_to_str(connection, show_type=False):
return "%s (%s)" % (connection.get_id(), connection.get_uuid())
def connections_filter(connections, profile_arg):
def devices_filter(devices, select_arg):
devices = list(sorted(devices, key=device_to_str))
if not select_arg:
return devices
# we preserve the order of the selected devices. And
# if devices are selected multiple times, we return
# them multiple times.
l = []
f = select_arg
for d in devices:
if f[0] == "iface":
if f[1] == d.get_iface():
l.append(d)
else:
assert f[0] == "*"
if f[1] is None:
l.append(d)
else:
if f[1] in [d.get_iface()]:
l.append(d)
return l
def connections_filter(connections, select_arg):
connections = list(sorted(connections, key=connection_to_str))
if not profile_arg:
if not select_arg:
return connections
# we preserve the order of the selected connections. And
# if connections are selected multiple times, we return
# them multiple times.
l = []
f = profile_arg
f = select_arg
for c in connections:
if f[0] == "id":
if f[1] == c.get_id():
@ -218,6 +391,7 @@ def ids_select(ids, mode, ids_arg):
if d not in requested:
requested.append(d)
else:
assert mode in [MODE_SET, MODE_APPLY]
d2 = d[0]
assert d2[0] in ["-", "+"]
d3 = d2[1:]
@ -241,7 +415,8 @@ def connection_print(connection, mode, ids_arg, dbus_path, prefix=""):
_print(
"%s%s [%s]" % (prefix, connection_to_str(connection, show_type=True), num_str)
)
_print("%s %s" % (prefix, dbus_path))
if dbus_path:
_print("%s %s" % (prefix, dbus_path))
if sett is not None:
dd = sett.get_property(NM.SETTING_OVS_EXTERNAL_IDS_DATA)
else:
@ -255,25 +430,7 @@ def connection_print(connection, mode, ids_arg, dbus_path, prefix=""):
_print('%s "%s" = <unset>' % (prefix, k))
def do_get(connections, ids_arg):
first_line = True
for c in connections:
if first_line:
first_line = False
else:
_print()
connection_print(c, MODE_GET, ids_arg, dbus_path=c.get_path())
def do_set(nmc, connection, ids_arg, do_test):
remote_connection = connection
connection = NM.SimpleConnection.new_clone(remote_connection)
connection_print(
connection, MODE_SET, [], remote_connection.get_path(), prefix="BEFORE: "
)
_print()
def sett_update(connection, ids_arg):
sett = connection.get_setting(NM.SettingOvsExternalIDs)
@ -334,34 +491,39 @@ def do_set(nmc, connection, ids_arg, do_test):
_print(' SET: "%s" = "%s" (unchanged)' % (key, val))
sett.set_data(key, val)
def do_get(connections, ids_arg):
first_line = True
for c in connections:
if first_line:
first_line = False
else:
_print()
connection_print(c, MODE_GET, ids_arg, dbus_path=c.get_path())
def do_set(nmc, connection, ids_arg, do_test):
remote_connection = connection
connection = NM.SimpleConnection.new_clone(remote_connection)
connection_print(
connection, MODE_SET, [], remote_connection.get_path(), prefix="BEFORE: "
)
_print()
sett_update(connection, ids_arg)
if do_test:
_print()
_print("Only show. Run without --test to set")
return
mainloop = GLib.MainLoop()
result_error = []
def callback(c, result):
try:
c.update2_finish(result)
except Exception as e:
result_error.append(e)
mainloop.quit()
remote_connection.update2(
connection.to_dbus(NM.ConnectionSerializationFlags.ALL),
NM.SettingsUpdate2Flags.NO_REAPPLY,
None,
None,
callback,
)
mainloop_run(mainloop=mainloop)
if result_error:
try:
connection_update2(remote_connection, connection)
except Exception as e:
_print()
_print("FAILURE to commit connection: %s" % (result_error[0]))
_print("FAILURE to commit connection: %s" % (e))
return
# NMClient received the completion of Update2() call. It also received
@ -397,6 +559,68 @@ def do_set(nmc, connection, ids_arg, do_test):
_print("WARNING: resulting connection is not as expected")
def do_apply(nmc, device, ids_arg, do_test):
try:
connection_orig, version_id = device_get_applied_connection(device)
except Exception as e:
_print(
'failure to get applied connection for %s: %s"' % (device_to_str(device), e)
)
die("The device does not seem active? Nothing to reapply")
_print(
"REAPPLY device %s (%s) with connection %s (version-id = %s)"
% (
device_to_str(device),
NM.Object.get_path(device),
connection_to_str(connection_orig),
version_id,
)
)
_print()
ovs_print_external_ids("BEFORE-OVS-VSCTL: ")
_print()
connection = NM.SimpleConnection.new_clone(connection_orig)
connection_print(connection, MODE_APPLY, [], device.get_path(), prefix="BEFORE: ")
_print()
sett_update(connection, ids_arg)
if do_test:
_print()
_print("Only show. Run without --test to set")
return
_print()
_print("reapply...")
try:
device_reapply(device, connection, version_id)
except Exception as e:
_print()
_print("FAILURE to commit connection: %s" % (e))
return
try:
connection_after, version_id = device_get_applied_connection(device)
except Exception as e:
_print(
'failure to get applied connection after reapply for device %s: %s"'
% (device_to_str(device), e)
)
die("FAILURE to get applied connection after reapply")
_print()
connection_print(connection, MODE_APPLY, [], device.get_path(), prefix="AFTER: ")
_print()
ovs_print_external_ids("AFTER-OVS-VSCTL: ")
###############################################################################
if __name__ == "__main__":
@ -405,21 +629,35 @@ if __name__ == "__main__":
nmc = NM.Client.new(None)
connections = connections_filter(nmc.get_connections(), args["profile_arg"])
if args["mode"] == MODE_APPLY:
if args["mode"] == MODE_SET:
if len(connections) != 1:
devices = devices_filter(nmc.get_devices(), args["select_arg"])
if len(devices) != 1:
_print(
"To set the external-ids of a connection, exactly one connection must be selected via id|uuid. Instead, %s connection matched ([%s])"
% (
len(connections),
", ".join([connection_to_str(c) for c in connections]),
)
"To apply the external-ids of a device, exactly one connection must be selected. Instead, %s devices matched ([%s])"
% (len(devices), ", ".join([device_to_str(c) for c in devices]),)
)
die_usage("Select unique connection to set")
do_set(nmc, connections[0], args["ids_arg"], do_test=args["do_test"])
die_usage("Select unique device to apply")
do_apply(nmc, devices[0], args["ids_arg"], do_test=args["do_test"])
else:
if len(connections) < 1:
_print("No connection selected for printing the external ids")
die_usage("Select connection to get")
do_get(connections, args["ids_arg"])
connections = connections_filter(nmc.get_connections(), args["select_arg"])
if args["mode"] == MODE_SET:
if len(connections) != 1:
_print(
"To set the external-ids of a connection, exactly one connection must be selected via id|uuid. Instead, %s connection matched ([%s])"
% (
len(connections),
", ".join([connection_to_str(c) for c in connections]),
)
)
die_usage("Select unique connection to set")
do_set(nmc, connections[0], args["ids_arg"], do_test=args["do_test"])
else:
if len(connections) < 1:
_print("No connection selected for printing the external ids")
die_usage("Select connection to get")
do_get(connections, args["ids_arg"])

View file

@ -478,7 +478,7 @@ nm_setting_ovs_external_ids_init(NMSettingOvsExternalIDs *self)
*
* Since: 1.30
*/
gpointer
NMSetting *
nm_setting_ovs_external_ids_new(void)
{
return g_object_new(NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NULL);

View file

@ -40,7 +40,7 @@ NM_AVAILABLE_IN_1_30
GType nm_setting_ovs_external_ids_get_type(void);
NM_AVAILABLE_IN_1_30
void *nm_setting_ovs_external_ids_new(void);
NMSetting *nm_setting_ovs_external_ids_new(void);
/*****************************************************************************/

View file

@ -925,6 +925,8 @@ _nm_g_slice_free_fcn_define(1) _nm_g_slice_free_fcn_define(2) _nm_g_slice_free_f
* error reason. Depending on the usage, this might indicate a bug because
* usually the target object should stay alive as long as there are pending
* operations.
* @NM_UTILS_ERROR_NOT_READY: the failure is related to being currently
* not ready to perform the operation.
*
* @NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular
* purpose during nm_device_check_connection_compatible() to indicate that
@ -945,6 +947,7 @@ typedef enum {
NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/
NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/
NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/
NM_UTILS_ERROR_NOT_READY, /*< nick=NotReady >*/
/* the following codes have a special meaning and are exactly used for
* nm_device_check_connection_compatible() and nm_device_check_connection_available().
@ -1662,6 +1665,19 @@ GPtrArray *_nm_g_ptr_array_copy(GPtrArray * array,
/*****************************************************************************/
static inline GHashTable *
nm_g_hash_table_ref(GHashTable *hash)
{
return hash ? g_hash_table_ref(hash) : NULL;
}
static inline void
nm_g_hash_table_unref(GHashTable *hash)
{
if (hash)
g_hash_table_unref(hash);
}
static inline guint
nm_g_hash_table_size(GHashTable *hash)
{

View file

@ -52,6 +52,7 @@
#include "settings/nm-settings-connection.h"
#include "settings/nm-settings.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-ovs-external-ids.h"
#include "nm-setting-user.h"
#include "nm-auth-utils.h"
#include "nm-keep-alive.h"
@ -12450,6 +12451,10 @@ can_reapply_change(NMDevice * self,
goto out_fail;
}
if (nm_streq(setting_name, NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME)
&& NM_DEVICE_GET_CLASS(self)->can_reapply_change_ovs_external_ids)
return TRUE;
out_fail:
g_set_error(error,
NM_DEVICE_ERROR,

View file

@ -457,6 +457,8 @@ typedef struct _NMDeviceClass {
bool act_stage1_prepare_set_hwaddr_ethernet : 1;
bool can_reapply_change_ovs_external_ids : 1;
} NMDeviceClass;
GType nm_device_get_type(void);

View file

@ -6,6 +6,8 @@
#include "nm-default.h"
#include "nm-device-ovs-bridge.h"
#include "nm-device-ovs-interface.h"
#include "nm-device-ovs-port.h"
#include "nm-ovsdb.h"
@ -13,6 +15,8 @@
#include "nm-active-connection.h"
#include "nm-setting-connection.h"
#include "nm-setting-ovs-bridge.h"
#include "nm-setting-ovs-external-ids.h"
#include "nm-core-internal.h"
#define _NMLOG_DEVICE_TYPE NMDeviceOvsBridge
#include "devices/nm-device-logging.h"
@ -87,6 +91,39 @@ static void
release_slave(NMDevice *device, NMDevice *slave, gboolean configure)
{}
void
nm_device_ovs_reapply_connection(NMDevice *self, NMConnection *con_old, NMConnection *con_new)
{
NMDeviceType device_type;
GType type;
nm_assert(NM_IS_DEVICE(self));
nm_assert(g_type_parent(G_TYPE_FROM_INSTANCE(self)) == NM_TYPE_DEVICE);
/* NMDevice's reapply_connection() doesn't do anything. No need to call the parent
* implementation. */
_LOGD(LOGD_DEVICE, "reapplying settings for OVS device");
type = G_OBJECT_TYPE(self);
if (type == NM_TYPE_DEVICE_OVS_INTERFACE)
device_type = NM_DEVICE_TYPE_OVS_INTERFACE;
else if (type == NM_TYPE_DEVICE_OVS_PORT)
device_type = NM_DEVICE_TYPE_OVS_PORT;
else {
nm_assert(type == NM_TYPE_DEVICE_OVS_BRIDGE);
device_type = NM_DEVICE_TYPE_OVS_BRIDGE;
}
nm_ovsdb_set_external_ids(
nm_ovsdb_get(),
device_type,
nm_device_get_ip_iface(self),
nm_connection_get_uuid(con_new),
_nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_EXTERNAL_IDS),
_nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS));
}
/*****************************************************************************/
static void
@ -114,12 +151,14 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass)
device_class->connection_type_check_compatible = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES();
device_class->is_master = TRUE;
device_class->get_type_description = get_type_description;
device_class->create_and_realize = create_and_realize;
device_class->unrealize = unrealize;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->enslave_slave = enslave_slave;
device_class->release_slave = release_slave;
device_class->is_master = TRUE;
device_class->get_type_description = get_type_description;
device_class->create_and_realize = create_and_realize;
device_class->unrealize = unrealize;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->enslave_slave = enslave_slave;
device_class->release_slave = release_slave;
device_class->can_reapply_change_ovs_external_ids = TRUE;
device_class->reapply_connection = nm_device_ovs_reapply_connection;
}

View file

@ -22,4 +22,9 @@ typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass;
GType nm_device_ovs_bridge_get_type(void);
/*****************************************************************************/
void
nm_device_ovs_reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_new);
#endif /* __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ */

View file

@ -6,6 +6,8 @@
#include "nm-default.h"
#include "nm-device-ovs-interface.h"
#include "nm-device-ovs-bridge.h"
#include "nm-ovsdb.h"
#include "devices/nm-device-private.h"
@ -382,17 +384,19 @@ nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *klass)
device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_OPENVSWITCH);
device_class->can_update_from_platform_link = can_update_from_platform_link;
device_class->deactivate = deactivate;
device_class->deactivate_async = deactivate_async;
device_class->get_type_description = get_type_description;
device_class->create_and_realize = create_and_realize;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->is_available = is_available;
device_class->check_connection_compatible = check_connection_compatible;
device_class->link_changed = link_changed;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->can_unmanaged_external_down = can_unmanaged_external_down;
device_class->set_platform_mtu = set_platform_mtu;
device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
device_class->can_update_from_platform_link = can_update_from_platform_link;
device_class->deactivate = deactivate;
device_class->deactivate_async = deactivate_async;
device_class->get_type_description = get_type_description;
device_class->create_and_realize = create_and_realize;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->is_available = is_available;
device_class->check_connection_compatible = check_connection_compatible;
device_class->link_changed = link_changed;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->can_unmanaged_external_down = can_unmanaged_external_down;
device_class->set_platform_mtu = set_platform_mtu;
device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
device_class->can_reapply_change_ovs_external_ids = TRUE;
device_class->reapply_connection = nm_device_ovs_reapply_connection;
}

View file

@ -6,7 +6,9 @@
#include "nm-default.h"
#include "nm-device-ovs-port.h"
#include "nm-device-ovs-interface.h"
#include "nm-device-ovs-bridge.h"
#include "nm-ovsdb.h"
#include "devices/nm-device-private.h"
@ -182,11 +184,13 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass)
device_class->connection_type_check_compatible = NM_SETTING_OVS_PORT_SETTING_NAME;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES();
device_class->is_master = TRUE;
device_class->get_type_description = get_type_description;
device_class->create_and_realize = create_and_realize;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->enslave_slave = enslave_slave;
device_class->release_slave = release_slave;
device_class->is_master = TRUE;
device_class->get_type_description = get_type_description;
device_class->create_and_realize = create_and_realize;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->enslave_slave = enslave_slave;
device_class->release_slave = release_slave;
device_class->can_reapply_change_ovs_external_ids = TRUE;
device_class->reapply_connection = nm_device_ovs_reapply_connection;
}

View file

@ -11,9 +11,11 @@
#include <gio/gunixsocketaddress.h>
#include "nm-glib-aux/nm-jansson.h"
#include "nm-glib-aux/nm-str-buf.h"
#include "nm-core-utils.h"
#include "nm-core-internal.h"
#include "devices/nm-device.h"
#include "nm-setting-ovs-external-ids.h"
/*****************************************************************************/
@ -51,57 +53,6 @@ typedef struct {
/*****************************************************************************/
enum { DEVICE_ADDED, DEVICE_REMOVED, INTERFACE_FAILED, LAST_SIGNAL };
static guint signals[LAST_SIGNAL] = {0};
typedef struct {
GSocketClient * client;
GSocketConnection *conn;
GCancellable * cancellable;
char buf[4096]; /* Input buffer */
size_t bufp; /* Last decoded byte in the input buffer. */
GString * input; /* JSON stream waiting for decoding. */
GString * output; /* JSON stream to be sent. */
guint64 call_id_counter;
GArray * calls; /* Method calls waiting for a response. */
GHashTable * interfaces; /* interface uuid => OpenvswitchInterface */
GHashTable * ports; /* port uuid => OpenvswitchPort */
GHashTable * bridges; /* bridge uuid => OpenvswitchBridge */
char * db_uuid;
guint num_failures;
} 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, gboolean retry, gboolean is_disposing);
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,
@ -112,6 +63,7 @@ typedef enum {
OVSDB_ADD_INTERFACE,
OVSDB_DEL_INTERFACE,
OVSDB_SET_INTERFACE_MTU,
OVSDB_SET_EXTERNAL_IDS,
} OvsdbCommand;
#define CALL_ID_UNSPEC G_MAXUINT64
@ -133,8 +85,87 @@ typedef union {
char * ifname;
guint32 mtu;
} set_interface_mtu;
struct {
NMDeviceType device_type;
char * ifname;
char * connection_uuid;
GHashTable * exid_old;
GHashTable * exid_new;
} set_external_ids;
} OvsdbMethodPayload;
typedef struct {
NMOvsdb * self;
CList calls_lst;
guint64 call_id;
OvsdbCommand command;
OvsdbMethodCallback callback;
gpointer user_data;
OvsdbMethodPayload payload;
} OvsdbMethodCall;
/*****************************************************************************/
enum { DEVICE_ADDED, DEVICE_REMOVED, INTERFACE_FAILED, LAST_SIGNAL };
static guint signals[LAST_SIGNAL] = {0};
typedef struct {
GSocketClient * client;
GSocketConnection *conn;
GCancellable * cancellable;
char buf[4096]; /* Input buffer */
size_t bufp; /* Last decoded byte in the input buffer. */
GString * input; /* JSON stream waiting for decoding. */
GString * output; /* JSON stream to be sent. */
guint64 call_id_counter;
CList calls_lst_head;
GHashTable *interfaces; /* interface uuid => OpenvswitchInterface */
GHashTable *ports; /* port uuid => OpenvswitchPort */
GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */
char * db_uuid;
guint num_failures;
} 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)
NM_DEFINE_SINGLETON_GETTER(NMOvsdb, nm_ovsdb_get, NM_TYPE_OVSDB);
/*****************************************************************************/
static void ovsdb_try_connect(NMOvsdb *self);
static void ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing);
static void ovsdb_read(NMOvsdb *self);
static void ovsdb_write(NMOvsdb *self);
static void ovsdb_next_command(NMOvsdb *self);
/*****************************************************************************/
#define _NMLOG_DOMAIN LOGD_DEVICE
#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "ovsdb", __VA_ARGS__)
#define _NMLOG_call(level, call, ...) \
_NMLOG((level), \
"call[" NM_HASH_OBFUSCATE_PTR_FMT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
NM_HASH_OBFUSCATE_PTR((call)) _NM_UTILS_MACRO_REST(__VA_ARGS__))
#define _LOGT_call(call, ...) _NMLOG_call(LOGL_TRACE, (call), __VA_ARGS__)
/*****************************************************************************/
#define OVSDB_METHOD_PAYLOAD_MONITOR() \
(&((const OvsdbMethodPayload){ \
.monitor = {}, \
@ -173,66 +204,55 @@ typedef union {
}, \
}))
typedef struct {
guint64 call_id;
OvsdbCommand command;
OvsdbMethodCallback callback;
gpointer user_data;
OvsdbMethodPayload payload;
} OvsdbMethodCall;
#define OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(xdevice_type, \
xifname, \
xconnection_uuid, \
xexid_old, \
xexid_new) \
(&((const OvsdbMethodPayload){ \
.set_external_ids = \
{ \
.device_type = xdevice_type, \
.ifname = (char *) NM_CONSTCAST(char, (xifname)), \
.connection_uuid = (char *) NM_CONSTCAST(char, (xconnection_uuid)), \
.exid_old = (xexid_old), \
.exid_new = (xexid_new), \
}, \
}))
/*****************************************************************************/
static NM_UTILS_LOOKUP_STR_DEFINE(_device_type_to_table,
NMDeviceType,
NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(NULL),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_TYPE_OVS_BRIDGE, "Bridge"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_TYPE_OVS_PORT, "Port"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_TYPE_OVS_INTERFACE,
"Interface"),
NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), );
/*****************************************************************************/
static void
_LOGT_call_do(const char *comment, OvsdbMethodCall *call, json_t *msg)
_call_complete(OvsdbMethodCall *call, json_t *response, GError *error)
{
gs_free char *msg_as_str = NULL;
if (response) {
gs_free char *str = NULL;
#define _QUOTE_MSG(msg, msg_as_str) \
(msg) ? ": " : "", (msg) ? (msg_as_str = json_dumps((msg), 0)) : ""
switch (call->command) {
case OVSDB_MONITOR:
_LOGT("%s: monitor%s%s", comment, _QUOTE_MSG(msg, msg_as_str));
break;
case OVSDB_ADD_INTERFACE:
_LOGT("%s: add-interface bridge=%s port=%s interface=%s%s%s",
comment,
nm_connection_get_interface_name(call->payload.add_interface.bridge),
nm_connection_get_interface_name(call->payload.add_interface.port),
nm_connection_get_interface_name(call->payload.add_interface.interface),
_QUOTE_MSG(msg, msg_as_str));
break;
case OVSDB_DEL_INTERFACE:
_LOGT("%s: del-interface interface=%s%s%s",
comment,
call->payload.del_interface.ifname,
_QUOTE_MSG(msg, msg_as_str));
break;
case OVSDB_SET_INTERFACE_MTU:
_LOGT("%s: set-interface-mtu interface=%s mtu=%u%s%s",
comment,
call->payload.set_interface_mtu.ifname,
call->payload.set_interface_mtu.mtu,
_QUOTE_MSG(msg, msg_as_str));
break;
str = json_dumps(response, 0);
if (error)
_LOGT_call(call, "completed: %s ; error: %s", str, error->message);
else
_LOGT_call(call, "completed: %s", str);
} else {
nm_assert(error);
_LOGT_call(call, "completed: error: %s", error->message);
}
}
#define _LOGT_call(comment, call, message) \
G_STMT_START \
{ \
if (_LOGT_ENABLED()) \
_LOGT_call_do((comment), (call), (message)); \
} \
G_STMT_END
c_list_unlink_stale(&call->calls_lst);
/*****************************************************************************/
static void
_clear_call(gpointer data)
{
OvsdbMethodCall *call = data;
if (call->callback)
call->callback(call->self, response, error, call->user_data);
switch (call->command) {
case OVSDB_MONITOR:
@ -250,7 +270,15 @@ _clear_call(gpointer data)
case OVSDB_SET_INTERFACE_MTU:
nm_clear_g_free(&call->payload.set_interface_mtu.ifname);
break;
case OVSDB_SET_EXTERNAL_IDS:
nm_clear_g_free(&call->payload.set_external_ids.ifname);
nm_clear_g_free(&call->payload.set_external_ids.connection_uuid);
nm_clear_pointer(&call->payload.set_external_ids.exid_old, g_hash_table_destroy);
nm_clear_pointer(&call->payload.set_external_ids.exid_new, g_hash_table_destroy);
break;
}
nm_g_slice_free(call);
}
/*****************************************************************************/
@ -341,24 +369,28 @@ ovsdb_call_method(NMOvsdb * self,
/* Ensure we're not unsynchronized before we queue the method call. */
ovsdb_try_connect(self);
if (add_first) {
g_array_prepend_val(priv->calls, (OvsdbMethodCall){});
call = &g_array_index(priv->calls, OvsdbMethodCall, 0);
} else
call = nm_g_array_append_new(priv->calls, OvsdbMethodCall);
call = g_slice_new(OvsdbMethodCall);
*call = (OvsdbMethodCall){
.self = self,
.call_id = CALL_ID_UNSPEC,
.command = command,
.callback = callback,
.user_data = user_data,
};
call->call_id = CALL_ID_UNSPEC;
call->command = command;
call->callback = callback;
call->user_data = user_data;
if (add_first)
c_list_link_front(&priv->calls_lst_head, &call->calls_lst);
else
c_list_link_tail(&priv->calls_lst_head, &call->calls_lst);
/* Mmigrate the arguments from @payload to @call->payload. Technically,
/* Migrate the arguments from @payload to @call->payload. Technically,
* this is not a plain copy, because
* - call->payload is not initialized (thus no need to free the previous data).
* - payload does not own the data. It is merely initialized using the
* OVSDB_METHOD_PAYLOAD_*() macros. */
switch (command) {
case OVSDB_MONITOR:
_LOGT_call(call, "new: monitor");
break;
case OVSDB_ADD_INTERFACE:
/* FIXME(applied-connection-immutable): we should not modify the applied
@ -373,18 +405,40 @@ ovsdb_call_method(NMOvsdb * self,
g_object_ref(payload->add_interface.bridge_device);
call->payload.add_interface.interface_device =
g_object_ref(payload->add_interface.interface_device);
_LOGT_call(call,
"new: add-interface bridge=%s port=%s interface=%s",
nm_connection_get_interface_name(call->payload.add_interface.bridge),
nm_connection_get_interface_name(call->payload.add_interface.port),
nm_connection_get_interface_name(call->payload.add_interface.interface));
break;
case OVSDB_DEL_INTERFACE:
call->payload.del_interface.ifname = g_strdup(payload->del_interface.ifname);
_LOGT_call(call, "new: del-interface interface=%s", call->payload.del_interface.ifname);
break;
case OVSDB_SET_INTERFACE_MTU:
call->payload.set_interface_mtu.ifname = g_strdup(payload->set_interface_mtu.ifname);
call->payload.set_interface_mtu.mtu = payload->set_interface_mtu.mtu;
_LOGT_call(call,
"new: set-interface-mtu interface=%s mtu=%u",
call->payload.set_interface_mtu.ifname,
call->payload.set_interface_mtu.mtu);
break;
case OVSDB_SET_EXTERNAL_IDS:
call->payload.set_external_ids.device_type = payload->set_external_ids.device_type;
call->payload.set_external_ids.ifname = g_strdup(payload->set_external_ids.ifname);
call->payload.set_external_ids.connection_uuid =
g_strdup(payload->set_external_ids.connection_uuid);
call->payload.set_external_ids.exid_old =
nm_g_hash_table_ref(payload->set_external_ids.exid_old);
call->payload.set_external_ids.exid_new =
nm_g_hash_table_ref(payload->set_external_ids.exid_new);
_LOGT_call(call,
"new: set-external-ids con-uuid=%s, interface=%s",
call->payload.set_external_ids.connection_uuid,
call->payload.set_external_ids.ifname);
break;
}
_LOGT_call("enqueue", call, NULL);
ovsdb_next_command(self);
}
@ -589,6 +643,94 @@ _set_port_interfaces(json_t *params, const char *ifname, json_t *new_interfaces)
ifname));
}
static json_t *
_j_create_external_ids_array_new(NMConnection *connection)
{
json_t * array;
const char *const * external_ids = NULL;
guint n_external_ids = 0;
guint i;
const char * uuid;
NMSettingOvsExternalIDs *s_exid;
nm_assert(NM_IS_CONNECTION(connection));
array = json_array();
uuid = nm_connection_get_uuid(connection);
nm_assert(uuid);
json_array_append_new(array, json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid));
s_exid = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_EXTERNAL_IDS);
if (s_exid)
external_ids = nm_setting_ovs_external_ids_get_data_keys(s_exid, &n_external_ids);
for (i = 0; i < n_external_ids; i++) {
const char *k = external_ids[i];
json_array_append_new(
array,
json_pack("[s, s]", k, nm_setting_ovs_external_ids_get_data(s_exid, k)));
}
return json_pack("[s, o]", "map", array);
}
static json_t *
_j_create_external_ids_array_update(const char *connection_uuid,
GHashTable *exid_old,
GHashTable *exid_new)
{
GHashTableIter iter;
json_t * mutations;
json_t * array;
const char * key;
const char * val;
nm_assert(connection_uuid);
mutations = json_array();
if (exid_old) {
array = NULL;
g_hash_table_iter_init(&iter, exid_old);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) {
if (nm_g_hash_table_contains(exid_new, key))
continue;
if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX))
continue;
if (!array)
array = json_array();
json_array_append_new(array, json_string(key));
}
if (array) {
json_array_append_new(
mutations,
json_pack("[s, s, [s, o]]", "external_ids", "delete", "set", array));
}
}
array = json_array();
json_array_append_new(
array,
json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, connection_uuid));
if (exid_new) {
g_hash_table_iter_init(&iter, exid_new);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX))
continue;
json_array_append_new(array, json_pack("[s, s]", key, val));
}
}
json_array_append_new(mutations,
json_pack("[s, s, [s, o]]", "external_ids", "insert", "map", array));
return mutations;
}
/**
* _insert_interface:
*
@ -639,7 +781,7 @@ _insert_interface(json_t * params,
json_array_append_new(options, json_array());
}
row = json_pack("{s:s, s:s, s:o, s:[s, [[s, s]]]}",
row = json_pack("{s:s, s:s, s:o, s:o}",
"name",
nm_connection_get_interface_name(interface),
"type",
@ -647,9 +789,7 @@ _insert_interface(json_t * params,
"options",
options,
"external_ids",
"map",
NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID,
nm_connection_get_uuid(interface));
_j_create_external_ids_array_new(interface));
if (cloned_mac)
json_object_set_new(row, "mac", json_string(cloned_mac));
@ -714,12 +854,7 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces)
json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(port)));
json_object_set_new(row, "interfaces", json_pack("[s, O]", "set", new_interfaces));
json_object_set_new(row,
"external_ids",
json_pack("[s, [[s, s]]]",
"map",
NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID,
nm_connection_get_uuid(port)));
json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(port));
/* Create a new one. */
json_array_append_new(params,
@ -779,12 +914,7 @@ _insert_bridge(json_t * params,
json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(bridge)));
json_object_set_new(row, "ports", json_pack("[s, O]", "set", new_ports));
json_object_set_new(row,
"external_ids",
json_pack("[s, [[s, s]]]",
"map",
NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID,
nm_connection_get_uuid(bridge)));
json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(bridge));
if (cloned_mac) {
json_object_set_new(row,
@ -1123,10 +1253,11 @@ ovsdb_next_command(NMOvsdb *self)
if (!priv->conn)
return;
if (!priv->calls->len)
if (c_list_is_empty(&priv->calls_lst_head))
return;
call = &g_array_index(priv->calls, OvsdbMethodCall, 0);
call = c_list_first_entry(&priv->calls_lst_head, OvsdbMethodCall, calls_lst);
if (call->call_id != CALL_ID_UNSPEC)
return;
@ -1201,6 +1332,25 @@ ovsdb_next_command(NMOvsdb *self)
"==",
call->payload.set_interface_mtu.ifname));
break;
case OVSDB_SET_EXTERNAL_IDS:
json_array_append_new(
params,
json_pack("{s:s, s:s, s:o, s:[[s, s, s]]}",
"op",
"mutate",
"table",
_device_type_to_table(call->payload.set_external_ids.device_type),
"mutations",
_j_create_external_ids_array_update(
call->payload.set_external_ids.connection_uuid,
call->payload.set_external_ids.exid_old,
call->payload.set_external_ids.exid_new),
"where",
"name",
"==",
call->payload.set_external_ids.ifname));
break;
default:
nm_assert_not_reached();
break;
@ -1218,9 +1368,9 @@ ovsdb_next_command(NMOvsdb *self)
}
g_return_if_fail(msg);
_LOGT_call("send", call, msg);
cmd = json_dumps(msg, 0);
_LOGT_call(call, "send: call-id=%" G_GUINT64_FORMAT ", %s", call->call_id, cmd);
g_string_append(priv->output, cmd);
free(cmd);
@ -1347,6 +1497,29 @@ _external_ids_equal(const GArray *arr1, const GArray *arr2)
return TRUE;
}
static char *
_external_ids_to_string(const GArray *arr)
{
NMStrBuf strbuf;
guint i;
if (!arr)
return g_strdup("empty");
nm_str_buf_init(&strbuf, NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE);
nm_str_buf_append(&strbuf, "[");
for (i = 0; i < arr->len; i++) {
const NMUtilsNamedValue *n = &g_array_index(arr, NMUtilsNamedValue, i);
if (i > 0)
nm_str_buf_append_c(&strbuf, ',');
nm_str_buf_append_printf(&strbuf, " \"%s\" = \"%s\"]", n->name, n->value_str);
}
nm_str_buf_append(&strbuf, " ]");
return nm_str_buf_finalize(&strbuf, NULL);
}
/*****************************************************************************/
/**
@ -1478,16 +1651,21 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
changed = TRUE;
}
if (changed) {
_LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s",
gs_free char *strtmp = NULL;
_LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s, external-ids=%s",
key,
type,
ovs_interface->name,
NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
", ",
ovs_interface->connection_uuid,
""));
""),
(strtmp = _external_ids_to_string(ovs_interface->external_ids)));
}
} else {
gs_free char *strtmp = NULL;
ovs_interface = g_slice_new(OpenvswitchInterface);
*ovs_interface = (OpenvswitchInterface){
.interface_uuid = g_strdup(key),
@ -1497,14 +1675,15 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
.external_ids = g_steal_pointer(&external_ids_arr),
};
g_hash_table_add(priv->interfaces, ovs_interface);
_LOGT("obj[iface:%s]: added an '%s' interface: %s%s%s",
_LOGT("obj[iface:%s]: added an '%s' interface: %s%s%s, external-ids=%s",
key,
ovs_interface->type,
ovs_interface->name,
NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
", ",
ovs_interface->connection_uuid,
""));
""),
(strtmp = _external_ids_to_string(ovs_interface->external_ids)));
if (_openvswitch_interface_should_emit_signal(ovs_interface))
_signal_emit_device_added(self, ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE);
}
@ -1585,15 +1764,20 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
changed = TRUE;
}
if (changed) {
_LOGT("obj[port:%s]: changed a port: %s%s%s",
gs_free char *strtmp = NULL;
_LOGT("obj[port:%s]: changed a port: %s%s%s, external-ids=%s",
key,
ovs_port->name,
NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid,
", ",
ovs_port->connection_uuid,
""));
""),
(strtmp = _external_ids_to_string(ovs_port->external_ids)));
}
} else {
gs_free char *strtmp = NULL;
ovs_port = g_slice_new(OpenvswitchPort);
*ovs_port = (OpenvswitchPort){
.port_uuid = g_strdup(key),
@ -1603,13 +1787,14 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
.external_ids = g_steal_pointer(&external_ids_arr),
};
g_hash_table_add(priv->ports, ovs_port);
_LOGT("obj[port:%s]: added a port: %s%s%s",
_LOGT("obj[port:%s]: added a port: %s%s%s, external-ids=%s",
key,
ovs_port->name,
NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid,
", ",
ovs_port->connection_uuid,
""));
""),
(strtmp = _external_ids_to_string(ovs_port->external_ids)));
_signal_emit_device_added(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT);
}
}
@ -1685,15 +1870,20 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
changed = TRUE;
}
if (changed) {
_LOGT("obj[bridge:%s]: changed a bridge: %s%s%s",
gs_free char *strtmp = NULL;
_LOGT("obj[bridge:%s]: changed a bridge: %s%s%s, external-ids=%s",
key,
ovs_bridge->name,
NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid,
", ",
ovs_bridge->connection_uuid,
""));
""),
(strtmp = _external_ids_to_string(ovs_bridge->external_ids)));
}
} else {
gs_free char *strtmp = NULL;
ovs_bridge = g_slice_new(OpenvswitchBridge);
*ovs_bridge = (OpenvswitchBridge){
.bridge_uuid = g_strdup(key),
@ -1703,13 +1893,14 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
.external_ids = g_steal_pointer(&external_ids_arr),
};
g_hash_table_add(priv->bridges, ovs_bridge);
_LOGT("obj[bridge:%s]: added a bridge: %s%s%s",
_LOGT("obj[bridge:%s]: added a bridge: %s%s%s, external-ids=%s",
key,
ovs_bridge->name,
NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid,
", ",
ovs_bridge->connection_uuid,
""));
""),
(strtmp = _external_ids_to_string(ovs_bridge->external_ids)));
_signal_emit_device_added(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE);
}
}
@ -1753,16 +1944,12 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
json_error_t json_error = {
0,
};
json_t * json_id = NULL;
json_int_t id = (json_int_t) -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;
gs_free_error GError *local = NULL;
json_t * json_id = NULL;
json_int_t id = (json_int_t) -1;
const char *method = NULL;
json_t * params = NULL;
json_t * result = NULL;
json_t * error = NULL;
if (json_unpack_ex(msg,
&json_error,
@ -1808,13 +1995,17 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
}
if (id >= 0) {
OvsdbMethodCall *call;
gs_free_error GError *local = NULL;
gs_free char * msg_as_str = NULL;
/* This is a response to a method call. */
if (!priv->calls->len) {
if (c_list_is_empty(&priv->calls_lst_head)) {
_LOGE("there are no queued calls expecting response %" G_GUINT64_FORMAT, (guint64) id);
ovsdb_disconnect(self, FALSE, FALSE);
return;
}
call = &g_array_index(priv->calls, OvsdbMethodCall, 0);
call = c_list_first_entry(&priv->calls_lst_head, OvsdbMethodCall, calls_lst);
if (call->call_id != id) {
_LOGE("expected a response to call %" G_GUINT64_FORMAT ", not %" G_GUINT64_FORMAT,
call->call_id,
@ -1824,7 +2015,7 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
}
/* Cool, we found a corresponding call. Finish it. */
_LOGT_call("response", call, msg);
_LOGT_call(call, "response: %s", (msg_as_str = json_dumps(msg, 0)));
if (!json_is_null(error)) {
/* The response contains an error. */
@ -1835,10 +2026,8 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
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);
_call_complete(call, result, local);
priv->num_failures = 0;
/* Don't progress further commands in case the callback hit an error
@ -2007,11 +2196,8 @@ ovsdb_write(NMOvsdb *self)
static void
ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing)
{
NMOvsdbPrivate * priv = NM_OVSDB_GET_PRIVATE(self);
OvsdbMethodCall * call;
OvsdbMethodCallback callback;
gpointer user_data;
gs_free_error GError *error = NULL;
NMOvsdbPrivate * priv = NM_OVSDB_GET_PRIVATE(self);
OvsdbMethodCall *call;
nm_assert(!retry || !is_disposing);
@ -2021,18 +2207,19 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing)
_LOGD("disconnecting from ovsdb, retry %d", retry);
if (retry) {
if (priv->calls->len != 0)
g_array_index(priv->calls, OvsdbMethodCall, 0).call_id = CALL_ID_UNSPEC;
} else {
nm_utils_error_set_cancelled(&error, is_disposing, "NMOvsdb");
while (priv->calls->len) {
call = &g_array_index(priv->calls, OvsdbMethodCall, priv->calls->len - 1);
callback = call->callback;
user_data = call->user_data;
g_array_remove_index(priv->calls, priv->calls->len - 1);
callback(self, NULL, error, user_data);
if (!c_list_is_empty(&priv->calls_lst_head)) {
call = c_list_first_entry(&priv->calls_lst_head, OvsdbMethodCall, calls_lst);
call->call_id = CALL_ID_UNSPEC;
}
} else {
gs_free_error GError *error = NULL;
if (is_disposing)
nm_utils_error_set_cancelled(&error, is_disposing, "NMOvsdb");
else
nm_utils_error_set(&error, NM_UTILS_ERROR_NOT_READY, "disconnected from ovsdb");
while ((call = c_list_last_entry(&priv->calls_lst_head, OvsdbMethodCall, calls_lst)))
_call_complete(call, NULL, error);
}
priv->bufp = 0;
@ -2231,6 +2418,36 @@ nm_ovsdb_set_interface_mtu(NMOvsdb * self,
OVSDB_METHOD_PAYLOAD_SET_INTERFACE_MTU(ifname, mtu));
}
void
nm_ovsdb_set_external_ids(NMOvsdb * self,
NMDeviceType device_type,
const char * ifname,
const char * connection_uuid,
NMSettingOvsExternalIDs *s_exid_old,
NMSettingOvsExternalIDs *s_exid_new)
{
gs_unref_hashtable GHashTable *exid_old = NULL;
gs_unref_hashtable GHashTable *exid_new = NULL;
exid_old = s_exid_old
? nm_utils_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_old))
: NULL;
exid_new = s_exid_new
? nm_utils_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_new))
: NULL;
ovsdb_call_method(self,
NULL,
NULL,
FALSE,
OVSDB_SET_EXTERNAL_IDS,
OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(device_type,
ifname,
connection_uuid,
exid_old,
exid_new));
}
/*****************************************************************************/
static void
@ -2238,8 +2455,8 @@ nm_ovsdb_init(NMOvsdb *self)
{
NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
priv->calls = g_array_new(FALSE, TRUE, sizeof(OvsdbMethodCall));
g_array_set_clear_func(priv->calls, _clear_call);
c_list_init(&priv->calls_lst_head);
priv->input = g_string_new(NULL);
priv->output = g_string_new(NULL);
priv->bridges =
@ -2260,6 +2477,8 @@ dispose(GObject *object)
ovsdb_disconnect(self, FALSE, TRUE);
nm_assert(c_list_is_empty(&priv->calls_lst_head));
if (priv->input) {
g_string_free(priv->input, TRUE);
priv->input = NULL;
@ -2268,10 +2487,6 @@ dispose(GObject *object)
g_string_free(priv->output, TRUE);
priv->output = NULL;
}
if (priv->calls) {
g_array_free(priv->calls, TRUE);
priv->calls = NULL;
}
nm_clear_pointer(&priv->bridges, g_hash_table_destroy);
nm_clear_pointer(&priv->ports, g_hash_table_destroy);

View file

@ -46,4 +46,13 @@ void nm_ovsdb_set_interface_mtu(NMOvsdb * self,
NMOvsdbCallback callback,
gpointer user_data);
struct _NMSettingOvsExternalIDs;
void nm_ovsdb_set_external_ids(NMOvsdb * self,
NMDeviceType device_type,
const char * ifname,
const char * connection_uuid,
struct _NMSettingOvsExternalIDs *s_exid_old,
struct _NMSettingOvsExternalIDs *s_exid_new);
#endif /* __NETWORKMANAGER_OVSDB_H__ */