ruby-dbus
ruby-dbus copied to clipboard
NetworkManager.Settings.Connection cannot round-trip, calling Update with what GetSettings returned
(reported by @imobachgs)
We would like to use ruby-dbus to interact with NetworkManager through D-Bus. The following example tries to use the method to Update a connection. It basically asks for the current configuration (GetSettings) an tries to apply the same configuration (calling Update).
require "dbus"
bus = ::DBus::SystemBus.instance
service = bus.service("org.freedesktop.NetworkManager")
settings_obj = service.object("/org/freedesktop/NetworkManager/Settings")
# find a settings object (a.k.a. connections)
settings_path, *_others = settings_obj["org.freedesktop.NetworkManager.Settings"].ListConnections.first
# set settings configuration
nm_settings = service.object(settings_path)
conn_settings = nm_settings["org.freedesktop.NetworkManager.Settings.Connection"].GetSettings.first
puts "Current configuration", conn_settings.inspect
nm_settings.Update(conn_settings)
However, it fails because data types for empty values (empty arrays and hashes) are not what NetworkManager expects. Our guess is that there is no typing information available, so ruby-dbus cannot figure out what to send in such cases.
/usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:364:in `block in send_sync_or_async': connection.permissions: wrong type; should be a list of strings.; caused by 3 sender=:1.7 -> dest=:1.499 serial=1943 reply_serial=12 path=; interface=; member= error_name=org.freedesktop.NetworkManager.Settings.Connection.InvalidProperty (DBus::Error)
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:552:in `process'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:486:in `send_sync'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:363:in `send_sync_or_async'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/proxy_object_interface.rb:70:in `block (2 levels) in define_method_from_descriptor'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/proxy_object.rb:113:in `call'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/proxy_object.rb:113:in `block (3 levels) in define_shortcut_methods'
from a.rb:16:in `<main>'
/usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:364:in `block in send_sync_or_async': connection.permissions: wrong type; should be a list of strings. (DBus::Error)
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:552:in `process'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:486:in `send_sync'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:363:in `send_sync_or_async'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/proxy_object_interface.rb:70:in `block (2 levels) in define_method_from_descriptor'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/proxy_object.rb:113:in `call'
from /usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/proxy_object.rb:113:in `block (3 levels) in define_shortcut_methods'
from change-nm.rb:16:in `<main>'
Even if we do not send any empty attribute (cleaning up the settings), we find an additional problem with ipv4.address-data:
/usr/lib64/ruby/gems/3.1.0/gems/ruby-dbus-0.18.1/lib/dbus/bus.rb:364:in `block in send_sync_or_async': ipv4.address-data: can't set property of type 'aa{sv}' from value of type 'av'; caused by 3 sender=:1.7 -> dest=:1.545 serial=2082 reply_serial=12 path=; interface=; member= error_name=org.freedesktop.NetworkManager.Settings.Connection.InvalidProperty (DBus::Error)
Are our assumptions right? Do we have a way to workaround this problem?
Thanks!
I've had success on my machine with applying this transformation between GetSettings and Update
def fix_types(settings)
types = {
"connection.permissions" => "as",
"802-11-wireless.mac-address" => "ay",
"802-11-wireless.mac-address-blacklist" => "as",
"802-11-wireless.ssid" => "ay",
"ipv4.address-data" => "aa{sv}",
"ipv4.addresses" => "aau",
"ipv4.dns" => "au",
"ipv4.dns-search" => "as",
"ipv4.route-data" => "aa{sv}",
"ipv4.routes" => "aau",
"ipv6.address-data" => "aa{sv}",
"ipv6.addresses" => "a(ayuay)",
"ipv6.dns" => "aay",
"ipv6.dns-search" => "as",
"ipv6.route-data" => "aa{sv}",
"ipv6.routes" => "a(ayuayu)"
}
types.each do |dot_prop, type|
section, prop = dot_prop.split "."
settings[section][prop] = ::DBus::Data.make_typed(type, settings[section][prop])
end
end
fix_types(conn_settings)
See https://www.rubydoc.info/gems/ruby-dbus/0.18.1/DBus/Data#make_typed-class_method
"A-Ayu-Ayu!" is my favorite type from now on!
In general, I'd like to improve the library so that you'll be able to specify that a method call (like GetSettings) should only return DBus::Data values instead of plain Ruby values. They need unwrapping but are the perfect fit for round-tripping like this.
It works just fine. Having an API to do this kind of type annotation is more than enough for us. Thanks!