diff options
Diffstat (limited to 'tools')
| -rwxr-xr-x | tools/check-docs.sh | 22 | ||||
| -rwxr-xr-x | tools/create-exports-NetworkManager.sh | 2 | ||||
| -rwxr-xr-x | tools/debug-helper.py | 10 | ||||
| -rwxr-xr-x | tools/run-nm-test.sh | 44 | ||||
| -rwxr-xr-x | tools/test-networkmanager-service.py | 2641 |
5 files changed, 1855 insertions, 864 deletions
diff --git a/tools/check-docs.sh b/tools/check-docs.sh new file mode 100755 index 00000000..9dcdb1ef --- /dev/null +++ b/tools/check-docs.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +SOURCEDIR=$1 +[ -n "$SOURCEDIR" ] && SOURCEDIR="$SOURCEDIR/" + +# Check that the D-Bus API docs contain all known interfaces +if (sed -n 's/.*<xi:include href="dbus-\(.*\.xml\)".*/\1\n\1/p' $SOURCEDIR''docs/api/network-manager-docs.xml; + cd $SOURCEDIR''introspection; ls *.xml) |sort |uniq -u| grep . >&2; then + echo "*** Error: D-Bus interfaces not included in docs/api/network-manager-docs.xml ***" >&2 + exit 1 +fi + +# Check that files that define types that are in public libnm API are included in libnm documentation. +# Don't complain about readability or I'll rewrite this in Perl. +if (sed -n 's/.*<xi:include href="\(xml\/.*\.xml\)".*/\1\n\1/p' $SOURCEDIR''docs/libnm/libnm-docs.xml; + grep -lE "$(sed -n 's/^[\t ]*\(.*_get_type\);/\1/p' $SOURCEDIR''libnm/libnm.ver |xargs echo |sed 's/ /|/g')" $SOURCEDIR''libnm{,-core}/*.h | + sed 's,.*/,xml/,;s/\.h$/.xml/') |sort |uniq -u| grep . >&2; then + echo "*** Error: libnm classes not included in docs/libnm/libnm-docs.xml ***" >&2 + exit 1 +fi + +exit 0 diff --git a/tools/create-exports-NetworkManager.sh b/tools/create-exports-NetworkManager.sh index b44e44bf..68752c54 100755 --- a/tools/create-exports-NetworkManager.sh +++ b/tools/create-exports-NetworkManager.sh @@ -19,7 +19,7 @@ die() { _build() { git clean -fdx ./autogen.sh --enable-ld-gc --enable-ifcfg-rh --enable-ifupdown \ - --enable-ifnet --enable-ibft --enable-teamdctl --enable-wifi \ + --enable-ibft --enable-teamdctl --enable-wifi \ --with-modem-manager-1 --with-ofono --with-more-asserts \ --with-more-logging make -j20 diff --git a/tools/debug-helper.py b/tools/debug-helper.py index 07338242..b841c39c 100755 --- a/tools/debug-helper.py +++ b/tools/debug-helper.py @@ -30,27 +30,27 @@ if args.do_set_nm_logging: dom_msg = "" if args.log_domains: dom_msg = " for domains: " + ','.join(args.log_domains) - print "Setting NetworkManager log level to '" + args.do_set_nm_logging + "'" + dom_msg + print("Setting NetworkManager log level to '" + args.do_set_nm_logging + "'" + dom_msg) nm_bus = bus.get_object('org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager') nm = dbus.Interface(nm_bus, dbus_interface='org.freedesktop.NetworkManager') nm.SetLogging(args.do_set_nm_logging, ','.join(args.log_domains)) if args.do_set_mm_logging: - print "Setting ModemManager log level to '" + args.do_set_mm_logging + "'" + print("Setting ModemManager log level to '" + args.do_set_mm_logging + "'") mm_bus = bus.get_object('org.freedesktop.ModemManager', '/org/freedesktop/ModemManager') mm = dbus.Interface(mm_bus, dbus_interface='org.freedesktop.ModemManager') mm.SetLogging(args.do_set_mm_logging) if args.do_set_wpa_logging: - print "Setting wpa_supplicant log level to '" + args.do_set_wpa_logging + "'" + print("Setting wpa_supplicant log level to '" + args.do_set_wpa_logging + "'") if 'debug' in args.do_set_wpa_logging or 'msgdump' in args.do_set_wpa_logging: - print "Enabling timestamps for wpasupplicant debugging logs" + print("Enabling timestamps for wpasupplicant debugging logs") use_timestamps = dbus.Boolean(True, variant_level=1) else: - print "Disabling timestamps for wpasupplicant debugging logs" + print("Disabling timestamps for wpasupplicant debugging logs") use_timestamps = dbus.Boolean(False, variant_level=1) wpa_bus = bus.get_object('fi.w1.wpa_supplicant1', '/fi/w1/wpa_supplicant1') diff --git a/tools/run-nm-test.sh b/tools/run-nm-test.sh index 5848d0a0..29ac7cd3 100755 --- a/tools/run-nm-test.sh +++ b/tools/run-nm-test.sh @@ -34,8 +34,17 @@ else CALLED_FROM_MAKE=0 fi +BUILDDIR= + if [ "$CALLED_FROM_MAKE" == 1 ]; then - NMTST_LIBTOOL=($1 --mode=execute); shift + BUILDDIR="$1" + shift + if [ -n "$1" ]; then + NMTST_LIBTOOL=($1 --mode=execute); + else + NMTST_LIBTOOL=() + fi + shift NMTST_VALGRIND_ARG="$1"; shift if [[ "$NMTST_VALGRIND_ARG" == no ]]; then NMTST_VALGRIND_ARG= @@ -121,6 +130,10 @@ else NMTST_USE_VALGRIND=0 shift; ;; + "-d") + NMTST_SET_DEBUG=1 + shift; + ;; "--test"|-t) shift TEST="$1" @@ -135,6 +148,7 @@ else ;; esac done + # we support calling the script directly. In this case, # only pass the path to the test to run. if test -z "${TEST+x}"; then @@ -144,6 +158,24 @@ else NMTST_SUPPRESSIONS="$SCRIPT_PATH/../valgrind.suppressions" fi + if [[ -z "$NMTST_BUILDDIR" ]]; then + if [[ "${NMTST_BUILDDIR-x}" == x ]]; then + # autodetect + BUILDDIR="$(readlink -f "$TEST")" + while [[ -n "$BUILDDIR" ]]; do + BUILDDIR="$(dirname "$BUILDDIR")" + [[ "$BUILDDIR" == / ]] && BUILDDIR= + [[ -z "$BUILDDIR" ]] && break + [[ -e "$BUILDDIR/libnm/.libs/libnm.so" ]] && break + [[ -e "$BUILDDIR/libnm/libnm.so" ]] && break + done + fi + fi + +fi + +if [ "$NMTST_SET_DEBUG" == 1 -a -z "${NMTST_DEBUG+x}" ]; then + export NMTST_DEBUG=d fi if _is_true "$NMTST_MAKE_FIRST" 0; then @@ -185,6 +217,16 @@ fi [ -x "$TEST" ] || die "Cannot execute test \"$TEST\"" +if [[ -n "$BUILDDIR" ]]; then + if [[ -d "$BUILDDIR/libnm" ]]; then + export GI_TYPELIB_PATH="$BUILDDIR/libnm/${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}" + if [[ -d "$BUILDDIR/libnm/.libs" ]]; then + export LD_LIBRARY_PATH="$BUILDDIR/libnm/.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + else + export LD_LIBRARY_PATH="$BUILDDIR/libnm${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + fi + fi +fi if ! _is_true "$NMTST_USE_VALGRIND" 0; then "${NMTST_DBUS_RUN_SESSION[@]}" \ diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 68097778..7fbed7ec 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -3,123 +3,651 @@ from __future__ import print_function -from gi.repository import GLib import sys + +import gi +from gi.repository import GLib + +try: + gi.require_version('NM', '1.0') + from gi.repository import NM +except Exception as e: + print("Cannot load gi.NM: %s" % (str(e))) + sys.exit(77) + +import os import dbus import dbus.service import dbus.mainloop.glib import random -import collections import uuid +import hashlib +import socket +import collections + +############################################################################### + +_DEFAULT_ARG = object() + +############################################################################### + +class Global: + pass + +gl = None + +############################################################################### + +class TestError(AssertionError): + def __init__(self, message = 'Unspecified error', errors = None): + AssertionError.__init__(self, message) + self.errors = errors + +############################################################################### + +class Util: + + PY3 = (sys.version_info[0] == 3) + + @staticmethod + def g_source_remove(source_id): + if source_id is not None: + GLib.source_remove(source_id) + + @staticmethod + def addr_family_check(family, allow_af_unspec = False): + if family == socket.AF_INET: + return + if family == socket.AF_INET6: + return + if allow_af_unspec and family == socket.AF_UNSPEC: + return + raise TestError('invalid address family %s' % (family)) + + @staticmethod + def ip_addr_pton(addr, family=None): + if addr is None: + return (None, None) + if family is not None and family is not socket.AF_UNSPEC: + Util.addr_family_check(family) + a = socket.inet_pton(family, addr) + else: + a = None + family = None + try: + a = socket.inet_pton(socket.AF_INET, addr) + family = socket.AF_INET + except: + a = socket.inet_pton(socket.AF_INET6, addr) + family = socket.AF_INET6 + if Util.PY3: + a = tuple([int(c) for c in a]) + else: + a = tuple([ord(c) for c in a]) + return (a, family) + + @staticmethod + def ip_addr_ntop(addr, family = None): + if Util.PY3: + a = bytes(addr) + else: + a = ''.join([chr(c) for c in addr]) + if len(a) == 4: + f = socket.AF_INET + elif len(a) == 16: + f = socket.AF_INET6 + else: + raise TestError("Invalid binary IP address '%s'" % (repr(addr))) + if family is not None and f != family: + raise TestError("Unexpected address family. Expected %s but ip address was %s" % (family, repr(addr))) + return socket.inet_ntop(f, a) + + @staticmethod + def ip_addr_norm(addr, family = None): + a, family = Util.ip_addr_pton(addr, family) + return (Util.ip_addr_ntop(a, family), family) + + @staticmethod + def ip4_addr_ne32(addr): + a, family = Util.ip_addr_pton(addr, socket.AF_INET) + n = 0 + for i in range(4): + n = (n << 8) + a[i] + return socket.htonl(n) + + @staticmethod + def ip6_addr_ay(addr): + return Util.ip_addr_pton(addr, socket.AF_INET6)[0] + + @staticmethod + def ip_net_parse(net, family = None): + parts = net.split('/') + if len(parts) != 2: + raise TestError("Invalid IP network '%s' has not '/' for the prefix length" % (net)) + prefix = int(parts[1]) + addr, family = Util.ip_addr_norm(parts[0], family) + if family == socket.AF_INET: + if prefix < 0 or prefix > 32: + raise TestError("Invalid prefix length for IPv4 address '%s'" % (net)) + else: + if prefix < 0 or prefix > 128: + raise TestError("Invalid prefix length for IPv4 address '%s'" % (net)) + return (addr, prefix, family) + + class RandomSeed(): + def __init__(self, seed): + self.cnt = 0 + self.seed = str(seed) + def _next(self): + c = self.cnt + self.cnt += 1 + return self.seed + '-' + str(c) + @staticmethod + def wrap(seed): + if seed is None: + return None + if isinstance(seed, Util.RandomSeed): + return seed + return Util.RandomSeed(seed) + @staticmethod + def get(seed, extra_seed = None): + if seed is None: + return None + if isinstance(seed, Util.RandomSeed): + seed = seed._next() + else: + seed = str(seed) + if extra_seed is None: + try: + extra_seed = Util.RandomSeed._extra_seed + except: + extra_seed = os.environ.get('NM_TEST_NETWORKMANAGER_SERVICE_SEED', '') + Util.RandomSeed._extra_seed = extra_seed + return extra_seed + seed + + @staticmethod + def random_stream(seed, length = None): + seed = Util.RandomSeed.wrap(seed) + # generates a stream of integers, in the range [0..255] + if seed is None: + # without a seed, we generate new random numbers. + while length is None or length > 0: + yield random.randint(0, 255) + if length is not None: + length -= 1 + return + v = None + while length is None or length > 0: + if not v: + s = Util.RandomSeed.get(seed) + s = s.encode('utf8') + v = hashlib.sha256(s).hexdigest() + yield int(v[0:2], 16) + v = v[2:] + if length is not None: + length -= 1 + + @staticmethod + def random_int(seed, v_start = _DEFAULT_ARG, v_end = _DEFAULT_ARG): + # - if neither start not end is give, return a number in the range + # u32 range [0, 0xFFFFFFFF] + # - if only start is given (the first argument), interpret it as + # the range of the interval. That is, return random number in + # range [0, start-1] + # - if end and start is given, return a random number with this + # range (inclusive!): [start, end] + if v_end is _DEFAULT_ARG: + # if only one edge is provided (no v_end), then the range + # is [0, v_start[. That is, random_int(seed, 5), returns + # values from 0 to 4. + if v_start is _DEFAULT_ARG: + # by default, return a 32u integer. + v_end = 0x100000000 + else: + v_end = v_start + v_start = 0 + else: + if v_start is _DEFAULT_ARG: + raise TestError("Cannot specify end without start") + # if a full range is provided, v_end is included. + # random_int(seed, 0, 4) returns values from 0 to 4. + v_end += 1 + n = 0 + span = v_end - v_start + assert span > 0 + for r in Util.random_stream(seed): + n = n * 256 + r + if n > span: + break + return v_start + (n % span) + + @staticmethod + def random_bool(seed): + return Util.random_int(seed, 0, 1) == 1 + + @staticmethod + def random_subset(seed, all_set): + all_set = list(all_set) + result = [] + seed = Util.RandomSeed.wrap(seed) + for i in list(range(Util.random_int(Util.RandomSeed.get(seed), len(all_set) + 1))): + idx = Util.random_int(Util.RandomSeed.get(seed), len(all_set)) + result.append(all_set[idx]) + del all_set[idx] + return result + + @staticmethod + def random_mac(seed): + return '%02X:%02X:%02X:%02X:%02X:%02X' % tuple(Util.random_stream(seed, 6)) + + @staticmethod + def random_ip(seed, net = None, family = None): + if net is not None: + mask, prefix, family = Util.ip_net_parse(net, family) + a_mask, unused = Util.ip_addr_pton(mask, family) + else: + prefix = None + Util.addr_family_check(family) + if family == socket.AF_INET: + l = 4 + else: + l = 16 + a = tuple(Util.random_stream(seed, l)) + if prefix is not None: + a2 = [] + for i in range(l): + if prefix == 0: + c = a[i] + elif prefix >= 8: + c = a_mask[i] + prefix -= 8 + else: + c = 0xFF & (0xFF << (8 - prefix)) + c = (a[i] & ~c) | (a_mask[i] & c) + prefix = 0 + a2.append(c) + a = tuple(a2) + return (Util.ip_addr_ntop(a, family), family) + + @staticmethod + def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + @staticmethod + def variant_from_dbus(val): + if isinstance(val, (dbus.String, str)): + return GLib.Variant('s', str(val)) + if isinstance(val, dbus.UInt32): + return GLib.Variant('u', int(val)) + if isinstance(val, dbus.Boolean): + return GLib.Variant('b', bool(val)) + if isinstance(val, dbus.Byte): + return GLib.Variant('y', int(val)) + if isinstance(val, dbus.Array): + try: + if val.signature == 's': + return GLib.Variant('as', [Util.variant_from_dbus(x) for x in val]) + if val.signature == 'b': + return GLib.Variant('ab', [Util.variant_from_dbus(x) for x in val]) + if val.signature == 'y': + return GLib.Variant('ay', [int(x) for x in val]) + if val.signature == 'u': + return GLib.Variant('au', [Util.variant_from_dbus(x) for x in val]) + if val.signature == 'ay': + return GLib.Variant('aay', [Util.variant_from_dbus(x) for x in val]) + if val.signature == 'au': + return GLib.Variant('aau', [Util.variant_from_dbus(x) for x in val]) + if val.signature == 'a{sv}': + return GLib.Variant('aa{sv}', [collections.OrderedDict([(str(k), Util.variant_from_dbus(v)) for k, v in addr.items()]) for addr in val]) + if val.signature == '(ayuay)': + return GLib.Variant('a(ayuay)', [Util.variant_from_dbus(x) for x in val]) + if val.signature == '(ayuayu)': + return GLib.Variant('a(ayuayu)', [Util.variant_from_dbus(x) for x in val]) + except Exception as e: + raise Exception("Cannot convert array element to type '%s': %s" % (val.signature, e.message)) + if isinstance(val, dbus.Dictionary): + if val.signature == 'ss': + return GLib.Variant('a{ss}', collections.OrderedDict([(str(k), str(v)) for k, v in val.items()])) + if val.signature == 'sv': + return GLib.Variant('a{sv}', collections.OrderedDict([(str(k), Util.variant_from_dbus(v)) for k, v in val.items()])) + if val.signature == 'sa{sv}': + c = collections.OrderedDict([ + (str(key1), + collections.OrderedDict([(str(key2), Util.variant_from_dbus(arr2)) for key2, arr2 in arr1.items()]) + ) for key1, arr1 in val.items() + ]) + return GLib.Variant('a{sa{sv}}', c) + + raise Exception("Unsupported type for value '%s'" % (repr(val))) + +############################################################################### + +IFACE_DBUS = 'org.freedesktop.DBus' +IFACE_OBJECT_MANAGER = 'org.freedesktop.DBus.ObjectManager' +IFACE_CONNECTION = 'org.freedesktop.NetworkManager.Settings.Connection' +IFACE_DEVICE = 'org.freedesktop.NetworkManager.Device' +IFACE_WIFI = 'org.freedesktop.NetworkManager.Device.Wireless' +IFACE_WIMAX = 'org.freedesktop.NetworkManager.Device.WiMax' +IFACE_TEST = 'org.freedesktop.NetworkManager.LibnmGlibTest' +IFACE_NM = 'org.freedesktop.NetworkManager' +IFACE_SETTINGS = 'org.freedesktop.NetworkManager.Settings' +IFACE_AGENT_MANAGER = 'org.freedesktop.NetworkManager.AgentManager' +IFACE_AGENT = 'org.freedesktop.NetworkManager.SecretAgent' +IFACE_WIRED = 'org.freedesktop.NetworkManager.Device.Wired' +IFACE_VLAN = 'org.freedesktop.NetworkManager.Device.Vlan' +IFACE_WIFI_AP = 'org.freedesktop.NetworkManager.AccessPoint' +IFACE_WIMAX_NSP = 'org.freedesktop.NetworkManager.WiMax.Nsp' +IFACE_ACTIVE_CONNECTION = 'org.freedesktop.NetworkManager.Connection.Active' +IFACE_VPN_CONNECTION = 'org.freedesktop.NetworkManager.VPN.Connection' +IFACE_DNS_MANAGER = 'org.freedesktop.NetworkManager.DnsManager' +IFACE_IP4_CONFIG = 'org.freedesktop.NetworkManager.IP4Config' +IFACE_IP6_CONFIG = 'org.freedesktop.NetworkManager.IP6Config' +IFACE_DHCP4_CONFIG = 'org.freedesktop.NetworkManager.DHCP4Config' +IFACE_DHCP6_CONFIG = 'org.freedesktop.NetworkManager.DHCP6Config' + +############################################################################### + +class BusErr: -mainloop = GLib.MainLoop() - -# NM State -NM_STATE_UNKNOWN = 0 -NM_STATE_ASLEEP = 10 -NM_STATE_DISCONNECTED = 20 -NM_STATE_DISCONNECTING = 30 -NM_STATE_CONNECTING = 40 -NM_STATE_CONNECTED_LOCAL = 50 -NM_STATE_CONNECTED_SITE = 60 -NM_STATE_CONNECTED_GLOBAL = 70 - -# Device state -NM_DEVICE_STATE_UNKNOWN = 0 -NM_DEVICE_STATE_UNMANAGED = 10 -NM_DEVICE_STATE_UNAVAILABLE = 20 -NM_DEVICE_STATE_DISCONNECTED = 30 -NM_DEVICE_STATE_PREPARE = 40 -NM_DEVICE_STATE_CONFIG = 50 -NM_DEVICE_STATE_NEED_AUTH = 60 -NM_DEVICE_STATE_IP_CONFIG = 70 -NM_DEVICE_STATE_IP_CHECK = 80 -NM_DEVICE_STATE_SECONDARIES = 90 -NM_DEVICE_STATE_ACTIVATED = 100 -NM_DEVICE_STATE_DEACTIVATING = 110 -NM_DEVICE_STATE_FAILED = 120 - -# Device type -NM_DEVICE_TYPE_UNKNOWN = 0 -NM_DEVICE_TYPE_ETHERNET = 1 -NM_DEVICE_TYPE_WIFI = 2 -NM_DEVICE_TYPE_UNUSED1 = 3 -NM_DEVICE_TYPE_UNUSED2 = 4 -NM_DEVICE_TYPE_BT = 5 -NM_DEVICE_TYPE_OLPC_MESH = 6 -NM_DEVICE_TYPE_WIMAX = 7 -NM_DEVICE_TYPE_MODEM = 8 -NM_DEVICE_TYPE_INFINIBAND = 9 -NM_DEVICE_TYPE_BOND = 10 -NM_DEVICE_TYPE_VLAN = 11 -NM_DEVICE_TYPE_ADSL = 12 -NM_DEVICE_TYPE_BRIDGE = 13 -NM_DEVICE_TYPE_GENERIC = 14 -NM_DEVICE_TYPE_TEAM = 15 - -# AC state -NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0 -NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1 -NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2 -NM_ACTIVE_CONNECTION_STATE_DEACTIVATING = 3 -NM_ACTIVE_CONNECTION_STATE_DEACTIVATED = 4 - -######################################################### -IFACE_DBUS = 'org.freedesktop.DBus' - -class UnknownInterfaceException(dbus.DBusException): - _dbus_error_name = IFACE_DBUS + '.UnknownInterface' - -class UnknownPropertyException(dbus.DBusException): - _dbus_error_name = IFACE_DBUS + '.UnknownProperty' - -def to_path_array(src): - array = dbus.Array([], signature=dbus.Signature('o')) - for o in src: - array.append(to_path(o)) - return array - -def to_path(src): - if src: - return dbus.ObjectPath(src.path) - return dbus.ObjectPath("/") + class UnknownInterfaceException(dbus.DBusException): + _dbus_error_name = IFACE_DBUS + '.UnknownInterface' + + class UnknownPropertyException(dbus.DBusException): + _dbus_error_name = IFACE_DBUS + '.UnknownProperty' + + class InvalidPropertyException(dbus.DBusException): + _dbus_error_name = IFACE_CONNECTION + '.InvalidProperty' + + class MissingPropertyException(dbus.DBusException): + _dbus_error_name = IFACE_CONNECTION + '.MissingProperty' + + class InvalidSettingException(dbus.DBusException): + _dbus_error_name = IFACE_CONNECTION + '.InvalidSetting' + + class MissingSettingException(dbus.DBusException): + _dbus_error_name = IFACE_CONNECTION + '.MissingSetting' + + class NotSoftwareException(dbus.DBusException): + _dbus_error_name = IFACE_DEVICE + '.NotSoftware' + + class ApNotFoundException(dbus.DBusException): + _dbus_error_name = IFACE_WIFI + '.AccessPointNotFound' + + class NspNotFoundException(dbus.DBusException): + _dbus_error_name = IFACE_WIMAX + '.NspNotFound' + + class PermissionDeniedException(dbus.DBusException): + _dbus_error_name = IFACE_NM + '.PermissionDenied' + + class UnknownDeviceException(dbus.DBusException): + _dbus_error_name = IFACE_NM + '.UnknownDevice' + + class UnknownConnectionException(dbus.DBusException): + _dbus_error_name = IFACE_NM + '.UnknownConnection' + + class InvalidHostnameException(dbus.DBusException): + _dbus_error_name = IFACE_SETTINGS + '.InvalidHostname' + + class NoSecretsException(dbus.DBusException): + _dbus_error_name = IFACE_AGENT_MANAGER + '.NoSecrets' + + class UserCanceledException(dbus.DBusException): + _dbus_error_name = IFACE_AGENT_MANAGER + '.UserCanceled' + + @staticmethod + def from_nmerror(e): + try: + domain, code = (e.domain, e.code) + except: + return None + if domain == GLib.quark_to_string(NM.ConnectionError.quark()): + if code == NM.ConnectionError.MISSINGSETTING: + return BusErr.MissingSettingException(e.message) + if code == NM.ConnectionError.INVALIDPROPERTY: + return BusErr.InvalidPropertyException(e.message) + return None + + @staticmethod + def raise_nmerror(e): + e2 = BusErr.from_nmerror(e) + if e2 is not None: + raise e2 + raise e + +############################################################################### + +class NmUtil: + + @staticmethod + def con_hash_to_connection(con_hash, do_verify = False, do_normalize = False): + + x_con = [] + for v_setting_name, v_setting in list(con_hash.items()): + if isinstance(v_setting_name, (dbus.String, str)): + v_setting_name = str(v_setting_name) + else: + raise Exception("Expected string dict, but got '%s' key" % (v_setting_name)) + x_setting = [] + for v_property_name, v_value in list(v_setting.items()): + if isinstance(v_property_name, (dbus.String, str)): + v_property_name = str(v_property_name) + else: + raise Exception("Expected string dict, but got '%s' subkey under %s (%s)" % (v_property_name, v_setting_name, repr(con_hash))) + try: + v = Util.variant_from_dbus(v_value) + except Exception as e: + raise Exception("Unsupported value %s.%s = %s (%s)" % (v_setting_name, v_property_name, v_value, str(e))) + x_setting.append((v_property_name, v)) + + x_con.append((v_setting_name, collections.OrderedDict(x_setting))) + + x_con = GLib.Variant('a{sa{sv}}', collections.OrderedDict(x_con)) + + assert GLib.Variant.equal(x_con, Util.variant_from_dbus(con_hash)) + + try: + con = NM.SimpleConnection.new_from_dbus(x_con) + except: + if do_verify: + raise + return None + + if do_normalize: + try: + con.normalize() + except: + if do_verify: + raise + + if do_verify: + con.verify() + + return con + + @staticmethod + def con_hash_verify(con_hash, do_verify_strict = True): + if NM.SETTING_CONNECTION_SETTING_NAME not in con_hash: + raise BusErr.MissingSettingException('connection: setting is required') + s_con = con_hash[NM.SETTING_CONNECTION_SETTING_NAME] + if NM.SETTING_CONNECTION_TYPE not in s_con: + raise BusErr.MissingPropertyException('connection.type: property is required') + if NM.SETTING_CONNECTION_UUID not in s_con: + raise BusErr.MissingPropertyException('connection.uuid: property is required') + if NM.SETTING_CONNECTION_ID not in s_con: + raise BusErr.MissingPropertyException('connection.id: property is required') + + if not do_verify_strict: + return; + t = s_con[NM.SETTING_CONNECTION_TYPE] + if t not in [ NM.SETTING_WIRED_SETTING_NAME, + NM.SETTING_WIRELESS_SETTING_NAME, + NM.SETTING_VLAN_SETTING_NAME, + NM.SETTING_WIMAX_SETTING_NAME, + NM.SETTING_VPN_SETTING_NAME ]: + raise BusErr.InvalidPropertyException('connection.type: unsupported connection type "%s"' % (t)) + + try: + con_nm = NmUtil.con_hash_to_connection(con_hash, do_verify = True, do_normalize = True) + except Exception as e: + BusErr.raise_nmerror(e) + + @staticmethod + def con_hash_get_id(con_hash): + if NM.SETTING_CONNECTION_SETTING_NAME in con_hash: + s_con = con_hash[NM.SETTING_CONNECTION_SETTING_NAME] + if NM.SETTING_CONNECTION_ID in s_con: + return s_con[NM.SETTING_CONNECTION_ID] + return None + + @staticmethod + def con_hash_get_uuid(con_hash): + if NM.SETTING_CONNECTION_SETTING_NAME in con_hash: + s_con = con_hash[NM.SETTING_CONNECTION_SETTING_NAME] + if NM.SETTING_CONNECTION_UUID in s_con: + return s_con[NM.SETTING_CONNECTION_UUID] + return None + + @staticmethod + def con_hash_get_type(con_hash): + if NM.SETTING_CONNECTION_SETTING_NAME in con_hash: + s_con = con_hash[NM.SETTING_CONNECTION_SETTING_NAME] + if NM.SETTING_CONNECTION_TYPE in s_con: + return s_con[NM.SETTING_CONNECTION_TYPE] + return None + +############################################################################### class ExportedObj(dbus.service.Object): - DBusInterface = collections.namedtuple('DBusInterface', ['dbus_iface', 'get_props_func', 'prop_changed_func']) + DBusInterface = collections.namedtuple('DBusInterface', ['dbus_iface', 'props', 'legacy_prop_changed_func']) + + @staticmethod + def create_path(klass, path_prefix = None): + if path_prefix is None: + path_prefix = klass.path_prefix + path = path_prefix + str(klass.path_counter_next) + klass.path_counter_next += 1 + return path + + @staticmethod + def to_path_array(src): + return dbus.Array([ExportedObj.to_path(o) for o in src] if src else [], + signature=dbus.Signature('o')) - def __init__(self, bus, object_path): - dbus.service.Object.__init__(self, bus, object_path) - self._bus = bus + @staticmethod + def to_path(src): + if src: + return dbus.ObjectPath(src.path) + return dbus.ObjectPath("/") + + def __init__(self, object_path, ident = None): + dbus.service.Object.__init__(self) + + self._dbus_ifaces = {} self.path = object_path - self.__ensure_dbus_ifaces() - object_manager.add_object(self) - def __ensure_dbus_ifaces(self): - if not hasattr(self, '_ExportedObj__dbus_ifaces'): - self.__dbus_ifaces = {} + # ident is an optional (unique) identifier for the instance. + # The test driver may set it to reference to the object by + # this identifier. For NetworkManager, the real ID of an + # object on D-Bus is the object_path. But that is generated + # by the stub server only after the test user created the + # object. The ident parameter may be specified by the user + # and thus can be hard-coded in the test. + if ident is None: + ident = object_path + self.ident = ident + + def export(self): + self.add_to_connection(gl.bus, self.path) + gl.object_manager.add_object(self) + + def unexport(self): + gl.object_manager.remove_object(self) + self.remove_from_connection() - def add_dbus_interface(self, dbus_iface, get_props_func, prop_changed_func): - self.__ensure_dbus_ifaces() - self.__dbus_ifaces[dbus_iface] = ExportedObj.DBusInterface(dbus_iface, get_props_func, prop_changed_func) + def dbus_interface_add(self, dbus_iface, props, legacy_prop_changed_func = None): + self._dbus_ifaces[dbus_iface] = ExportedObj.DBusInterface(dbus_iface, props, legacy_prop_changed_func) - def __dbus_interface_get(self, dbus_iface): - if dbus_iface not in self.__dbus_ifaces: - raise UnknownInterfaceException() - return self.__dbus_ifaces[dbus_iface] + def _dbus_interface_get(self, dbus_iface): + if dbus_iface not in self._dbus_ifaces: + raise BusErr.UnknownInterfaceException() + return self._dbus_ifaces[dbus_iface] - def _dbus_property_get(self, dbus_iface, propname = None): - props = self.__dbus_interface_get(dbus_iface).get_props_func() + def _dbus_interface_get_property(self, dbus_interface, propname = None): + props = dbus_interface.props if propname is None: return props if propname not in props: - raise UnknownPropertyException() + raise BusErr.UnknownPropertyException() return props[propname] + def _dbus_property_get(self, dbus_iface, propname = None): + return self._dbus_interface_get_property(self._dbus_interface_get(dbus_iface), + propname) + + def _dbus_property_set(self, dbus_iface, propname, value, allow_detect_dbus_iface = False, dry_run = False, force_update = False): + if allow_detect_dbus_iface and not dbus_iface: + props = None + for p, dbus_interface in self._dbus_ifaces.items(): + if propname in dbus_interface.props: + if props is not None: + raise TestError("Cannot uniquely find the property '%s' on object '%s'" % (propname, self.path)) + props = dbus_interface.props + dbus_iface = p + if props is None: + raise TestError("Cannot find the property '%s' on object '%s'" % (propname, self.path)) + else: + try: + dbus_interface = self._dbus_interface_get(dbus_iface) + props = self._dbus_interface_get_property(dbus_interface) + except: + if dry_run: + raise TestError("No interface '%s' on '%s'" % (dbus_iface, self.path)) + raise + + if dry_run: + if propname not in props: + raise TestError("No property '%s' on '%s' on '%s'" % (propname, dbus_iface, self.path)) + + permission_granted = False + + if isinstance(self, ActiveConnection): + if dbus_iface == IFACE_ACTIVE_CONNECTION: + if propname == PRP_ACTIVE_CONNECTION_STATE: + permission_granted = True + elif dbus_iface == IFACE_VPN_CONNECTION: + if propname == PRP_VPN_CONNECTION_VPN_STATE: + permission_granted = True + + if not permission_granted: + raise TestError("Cannot set property '%s' on '%s' on '%s' via D-Bus" % (propname, dbus_iface, self.path)) + + return + + assert propname in props + + if not force_update: + if props[propname] == value: + return + + props[propname] = value + self._dbus_property_notify(dbus_iface, propname) + def _dbus_property_notify(self, dbus_iface, propname): - prop = self._dbus_property_get(dbus_iface, propname) - self.__dbus_interface_get(dbus_iface).prop_changed_func(self, { propname: prop }) - ExportedObj.PropertiesChanged(self, dbus_iface, { propname: prop }, []) + dbus_interface = self._dbus_interface_get(dbus_iface) + prop = self._dbus_interface_get_property(dbus_interface, propname) + if propname is not None: + prop = { propname: prop } + ExportedObj.PropertiesChanged(self, dbus_iface, prop, []) + + # the legacy_prop_changed_func signal is a legacy signal that got obsoleted by the standard + # PropertiesChanged signal. NetworkManager (and this stub) still emit it for backward + # compatibility reasons. Note that this stub server implementation gets this wrong, + # for example, it emits PropertiesChanged signal on org.freedesktop.NetworkManager.Device, + # which NetworkManager never did. + # See https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/src/nm-dbus-manager.c?id=db80d5f62a1edf39c5970887ef7b9ec62dd4163f#n1274 + if dbus_interface.legacy_prop_changed_func is not None: + dbus_interface.legacy_prop_changed_func(self, prop) @dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as') def PropertiesChanged(self, iface, changed, invalidated): @@ -135,74 +663,89 @@ class ExportedObj(dbus.service.Object): def get_managed_ifaces(self): my_ifaces = {} - for iface in self.__dbus_ifaces: - my_ifaces[iface] = self.__dbus_ifaces[iface].get_props_func() - return self.path, my_ifaces - - def remove_from_connection(self): - object_manager.remove_object(self) - dbus.service.Object.remove_from_connection(self) - -################################################################### -IFACE_DEVICE = 'org.freedesktop.NetworkManager.Device' - -class NotSoftwareException(dbus.DBusException): - _dbus_error_name = IFACE_DEVICE + '.NotSoftware' - -PD_UDI = "Udi" -PD_IFACE = "Interface" -PD_DRIVER = "Driver" -PD_STATE = "State" -PD_ACTIVE_CONNECTION = "ActiveConnection" -PD_IP4_CONFIG = "Ip4Config" -PD_IP6_CONFIG = "Ip6Config" -PD_DHCP4_CONFIG = "Dhcp4Config" -PD_DHCP6_CONFIG = "Dhcp6Config" -PD_MANAGED = "Managed" -PD_AUTOCONNECT = "Autoconnect" -PD_DEVICE_TYPE = "DeviceType" -PD_AVAILABLE_CONNECTIONS = "AvailableConnections" + for iface in self._dbus_ifaces: + my_ifaces[iface] = self._dbus_ifaces[iface].props + return my_ifaces + +############################################################################### + +PRP_DEVICE_UDI = "Udi" +PRP_DEVICE_IFACE = "Interface" +PRP_DEVICE_DRIVER = "Driver" +PRP_DEVICE_STATE = "State" +PRP_DEVICE_ACTIVE_CONNECTION = "ActiveConnection" +PRP_DEVICE_IP4_CONFIG = "Ip4Config" +PRP_DEVICE_IP6_CONFIG = "Ip6Config" +PRP_DEVICE_DHCP4_CONFIG = "Dhcp4Config" +PRP_DEVICE_DHCP6_CONFIG = "Dhcp6Config" +PRP_DEVICE_MANAGED = "Managed" +PRP_DEVICE_AUTOCONNECT = "Autoconnect" +PRP_DEVICE_DEVICE_TYPE = "DeviceType" +PRP_DEVICE_AVAILABLE_CONNECTIONS = "AvailableConnections" class Device(ExportedObj): - counter = 1 - def __init__(self, bus, iface, devtype): - object_path = "/org/freedesktop/NetworkManager/Devices/%d" % Device.counter - Device.counter = Device.counter + 1 + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/Devices/" + + def __init__(self, iface, devtype, ident = None): + + if ident is None: + ident = iface + + ExportedObj.__init__(self, ExportedObj.create_path(Device), ident) - self.iface = iface - self.udi = "/sys/devices/virtual/%s" % iface - self.devtype = devtype - self.active_connection = None - self.state = NM_DEVICE_STATE_UNAVAILABLE self.ip4_config = None self.ip6_config = None self.dhcp4_config = None self.dhcp6_config = None - self.available_connections = [] - - self.add_dbus_interface(IFACE_DEVICE, self.__get_props, Device.PropertiesChanged) - ExportedObj.__init__(self, bus, object_path) - - # Properties interface - def __get_props(self): - props = {} - props[PD_UDI] = self.udi - props[PD_IFACE] = self.iface - props[PD_DRIVER] = "virtual" - props[PD_STATE] = dbus.UInt32(self.state) - props[PD_ACTIVE_CONNECTION] = to_path(self.active_connection) - props[PD_IP4_CONFIG] = to_path(self.ip4_config) - props[PD_IP6_CONFIG] = to_path(self.ip6_config) - props[PD_DHCP4_CONFIG] = to_path(self.dhcp4_config) - props[PD_DHCP6_CONFIG] = to_path(self.dhcp6_config) - props[PD_MANAGED] = True - props[PD_AUTOCONNECT] = True - props[PD_DEVICE_TYPE] = dbus.UInt32(self.devtype) - props[PD_AVAILABLE_CONNECTIONS] = to_path_array(self.available_connections) - return props - - # methods + + props = { + PRP_DEVICE_UDI: "/sys/devices/virtual/%s" % (iface), + PRP_DEVICE_IFACE: iface, + PRP_DEVICE_DRIVER: "virtual", + PRP_DEVICE_STATE: dbus.UInt32(NM.DeviceState.UNAVAILABLE), + PRP_DEVICE_ACTIVE_CONNECTION: ExportedObj.to_path(None), + PRP_DEVICE_IP4_CONFIG: ExportedObj.to_path(self.ip4_config), + PRP_DEVICE_IP6_CONFIG: ExportedObj.to_path(self.ip6_config), + PRP_DEVICE_DHCP4_CONFIG: ExportedObj.to_path(self.dhcp4_config), + PRP_DEVICE_DHCP6_CONFIG: ExportedObj.to_path(self.dhcp6_config), + PRP_DEVICE_MANAGED: True, + PRP_DEVICE_AUTOCONNECT: True, + PRP_DEVICE_DEVICE_TYPE: dbus.UInt32(devtype), + PRP_DEVICE_AVAILABLE_CONNECTIONS: ExportedObj.to_path_array([]), + } + + self.dbus_interface_add(IFACE_DEVICE, props, Device.PropertiesChanged) + + def start(self): + self.ip4_config = IP4Config() + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_IP4_CONFIG, ExportedObj.to_path(self.ip4_config)) + self.ip6_config = IP6Config() + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_IP6_CONFIG, ExportedObj.to_path(self.ip6_config)) + self.dhcp4_config = Dhcp4Config() + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_DHCP4_CONFIG, ExportedObj.to_path(self.dhcp4_config)) + self.dhcp6_config = Dhcp6Config() + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_DHCP6_CONFIG, ExportedObj.to_path(self.dhcp6_config)) + + def stop(self): + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_IP4_CONFIG, ExportedObj.to_path(None)) + if self.ip4_config is not None: + self.ip4_config.unexport() + self.ip4_config = None + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_IP6_CONFIG, ExportedObj.to_path(None)) + if self.ip6_config is not None: + self.ip6_config.unexport() + self.ip6_config = None + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_DHCP4_CONFIG, ExportedObj.to_path(None)) + if self.dhcp4_config is not None: + self.dhcp4_config.unexport() + self.dhcp4_config = None + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_DHCP6_CONFIG, ExportedObj.to_path(None)) + if self.dhcp6_config is not None: + self.dhcp6_config.unexport() + self.dhcp6_config = None + @dbus.service.method(dbus_interface=IFACE_DEVICE, in_signature='', out_signature='') def Disconnect(self): pass @@ -210,197 +753,209 @@ class Device(ExportedObj): @dbus.service.method(dbus_interface=IFACE_DEVICE, in_signature='', out_signature='') def Delete(self): # We don't currently support any software device types, so... - raise NotSoftwareException() + raise BusErr.NotSoftwareException() pass - def __notify(self, propname): - self._dbus_property_notify(IFACE_DEVICE, propname) - @dbus.service.signal(IFACE_DEVICE, signature='a{sv}') def PropertiesChanged(self, changed): pass def set_active_connection(self, ac): - self.active_connection = ac - self.__notify(PD_ACTIVE_CONNECTION) - -################################################################### - -def random_mac(): - return '%02X:%02X:%02X:%02X:%02X:%02X' % ( - random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), - random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) - ) - -################################################################### -IFACE_WIRED = 'org.freedesktop.NetworkManager.Device.Wired' - -PE_HW_ADDRESS = "HwAddress" -PE_PERM_HW_ADDRESS = "PermHwAddress" -PE_SPEED = "Speed" -PE_CARRIER = "Carrier" -PE_S390_SUBCHANNELS = "S390Subchannels" + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_ACTIVE_CONNECTION, ac) + + def connection_is_available(self, con_inst): + if con_inst.is_vpn(): + return False + if isinstance(self, WiredDevice): + if con_inst.get_type() == NM.SETTING_WIRED_SETTING_NAME: + return True + elif isinstance(self, WifiDevice): + if con_inst.get_type() == NM.SETTING_WIRELESS_SETTING_NAME: + return True + return False + + def available_connections_get(self): + return [c for c in gl.settings.get_connections() if self.connection_is_available(c)] + + def available_connections_update(self): + self._dbus_property_set(IFACE_DEVICE, PRP_DEVICE_AVAILABLE_CONNECTIONS, + ExportedObj.to_path_array(self.available_connections_get())) + +############################################################################### + +PRP_WIRED_HW_ADDRESS = "HwAddress" +PRP_WIRED_PERM_HW_ADDRESS = "PermHwAddress" +PRP_WIRED_SPEED = "Speed" +PRP_WIRED_CARRIER = "Carrier" +PRP_WIRED_S390_SUBCHANNELS = "S390Subchannels" class WiredDevice(Device): - def __init__(self, bus, iface, mac, subchannels): + def __init__(self, iface, mac = None, subchannels = None, ident = None): + Device.__init__(self, iface, NM.DeviceType.ETHERNET, ident) if mac is None: - self.mac = random_mac() - else: - self.mac = mac - self.carrier = False - self.s390_subchannels = subchannels - - self.add_dbus_interface(IFACE_WIRED, self.__get_props, WiredDevice.PropertiesChanged) - Device.__init__(self, bus, iface, NM_DEVICE_TYPE_ETHERNET) - - # Properties interface - def __get_props(self): - props = {} - props[PE_HW_ADDRESS] = self.mac - props[PE_PERM_HW_ADDRESS] = self.mac - props[PE_SPEED] = dbus.UInt32(100) - props[PE_CARRIER] = self.carrier - props[PE_S390_SUBCHANNELS] = self.s390_subchannels - return props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_WIRED, propname) + mac = Util.random_mac(self.ident) + if subchannels is None: + subchannels = dbus.Array(signature = 's') + + props = { + PRP_WIRED_HW_ADDRESS: mac, + PRP_WIRED_PERM_HW_ADDRESS: mac, + PRP_WIRED_SPEED: dbus.UInt32(100), + PRP_WIRED_CARRIER: False, + PRP_WIRED_S390_SUBCHANNELS: subchannels, + } + + self.dbus_interface_add(IFACE_WIRED, props, WiredDevice.PropertiesChanged) @dbus.service.signal(IFACE_WIRED, signature='a{sv}') def PropertiesChanged(self, changed): pass -################################################################### -IFACE_VLAN = 'org.freedesktop.NetworkManager.Device.Vlan' +############################################################################### -PV_HW_ADDRESS = "HwAddress" -PV_CARRIER = "Carrier" -PV_VLAN_ID = "VlanId" +PRP_VLAN_HW_ADDRESS = "HwAddress" +PRP_VLAN_CARRIER = "Carrier" +PRP_VLAN_VLAN_ID = "VlanId" class VlanDevice(Device): - def __init__(self, bus, iface): - self.mac = random_mac() - self.carrier = False - self.vlan_id = 1 - - self.add_dbus_interface(IFACE_VLAN, self.__get_props, VlanDevice.PropertiesChanged) - Device.__init__(self, bus, iface, NM_DEVICE_TYPE_VLAN) - - # Properties interface - def __get_props(self): - props = {} - props[PV_HW_ADDRESS] = self.mac - props[PV_CARRIER] = self.carrier - props[PV_VLAN_ID] = dbus.UInt32(self.vlan_id) - return props + def __init__(self, iface, ident = None): + Device.__init__(self, iface, NM.DeviceType.VLAN, ident) + + props = { + PRP_VLAN_HW_ADDRESS: Util.random_mac(self.ident), + PRP_VLAN_CARRIER: False, + PRP_VLAN_VLAN_ID: dbus.UInt32(1), + } + + self.dbus_interface_add(IFACE_VLAN, props, VlanDevice.PropertiesChanged) @dbus.service.signal(IFACE_VLAN, signature='a{sv}') def PropertiesChanged(self, changed): pass -################################################################### -IFACE_WIFI_AP = 'org.freedesktop.NetworkManager.AccessPoint' +############################################################################### -PP_FLAGS = "Flags" -PP_WPA_FLAGS = "WpaFlags" -PP_RSN_FLAGS = "RsnFlags" -PP_SSID = "Ssid" -PP_FREQUENCY = "Frequency" -PP_HW_ADDRESS = "HwAddress" -PP_MODE = "Mode" -PP_MAX_BITRATE = "MaxBitrate" -PP_STRENGTH = "Strength" +PRP_WIFI_AP_FLAGS = "Flags" +PRP_WIFI_AP_WPA_FLAGS = "WpaFlags" +PRP_WIFI_AP_RSN_FLAGS = "RsnFlags" +PRP_WIFI_AP_SSID = "Ssid" +PRP_WIFI_AP_FREQUENCY = "Frequency" +PRP_WIFI_AP_HW_ADDRESS = "HwAddress" +PRP_WIFI_AP_MODE = "Mode" +PRP_WIFI_AP_MAX_BITRATE = "MaxBitrate" +PRP_WIFI_AP_STRENGTH = "Strength" +PRP_WIFI_AP_LAST_SEEN = "LastSeen" class WifiAp(ExportedObj): - counter = 0 - def __init__(self, bus, ssid, mac, flags, wpaf, rsnf, freq): - path = "/org/freedesktop/NetworkManager/AccessPoint/%d" % WifiAp.counter - WifiAp.counter = WifiAp.counter + 1 + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/AccessPoint/" + + def __init__(self, ssid, bssid = None, flags = None, wpaf = None, rsnf = None, freq = None, strength = None, ident = None): + + ExportedObj.__init__(self, ExportedObj.create_path(WifiAp), ident) + + if flags is None: + flags = 0x1 + if wpaf is None: + wpaf = 0x1cc + if rsnf is None: + rsnf = 0x1cc + if freq is None: + freq = 2412 + if bssid is None: + bssid = Util.random_mac(self.path) + if strength is None: + strength = Util.random_int(self.path, 100) self.ssid = ssid - if mac: - self.bssid = mac - else: - self.bssid = random_mac() - self.flags = flags - self.wpaf = wpaf - self.rsnf = rsnf - self.freq = freq - self.strength = random.randint(0, 100) - self.strength_id = GLib.timeout_add_seconds(10, self.strength_cb, None) - - self.add_dbus_interface(IFACE_WIFI_AP, self.__get_props, WifiAp.PropertiesChanged) - ExportedObj.__init__(self, bus, path) - - def __del__(self): - if self.strength_id > 0: - GLib.source_remove(self.strength_id) - self.strength_id = 0 - - def strength_cb(self, ignored): - self.strength = random.randint(0, 100) - self.__notify(PP_STRENGTH) - return True - - # Properties interface - def __get_props(self): - props = {} - props[PP_FLAGS] = dbus.UInt32(self.flags) - props[PP_WPA_FLAGS] = dbus.UInt32(self.wpaf) - props[PP_RSN_FLAGS] = dbus.UInt32(self.rsnf) - props[PP_SSID] = dbus.ByteArray(self.ssid.encode('utf-8')) - props[PP_FREQUENCY] = dbus.UInt32(self.freq) - props[PP_HW_ADDRESS] = self.bssid - props[PP_MODE] = dbus.UInt32(2) # NM_802_11_MODE_INFRA - props[PP_MAX_BITRATE] = dbus.UInt32(54000) - props[PP_STRENGTH] = dbus.Byte(self.strength) - return props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_WIFI_AP, propname) + + props = { + PRP_WIFI_AP_FLAGS: dbus.UInt32(flags), + PRP_WIFI_AP_WPA_FLAGS: dbus.UInt32(wpaf), + PRP_WIFI_AP_RSN_FLAGS: dbus.UInt32(rsnf), + PRP_WIFI_AP_SSID: dbus.ByteArray(self.ssid.encode('utf-8')), + PRP_WIFI_AP_FREQUENCY: dbus.UInt32(freq), + PRP_WIFI_AP_HW_ADDRESS: bssid, + PRP_WIFI_AP_MODE: dbus.UInt32(getattr(NM,'80211Mode').INFRA), + PRP_WIFI_AP_MAX_BITRATE: dbus.UInt32(54000), + PRP_WIFI_AP_STRENGTH: dbus.Byte(strength), + PRP_WIFI_AP_LAST_SEEN: dbus.Int32(NM.utils_get_timestamp_msec() / 1000), + } + + self.dbus_interface_add(IFACE_WIFI_AP, props, WifiAp.PropertiesChanged) @dbus.service.signal(IFACE_WIFI_AP, signature='a{sv}') def PropertiesChanged(self, changed): pass -################################################################### -IFACE_WIFI = 'org.freedesktop.NetworkManager.Device.Wireless' +############################################################################### -class ApNotFoundException(dbus.DBusException): - _dbus_error_name = IFACE_WIFI + '.AccessPointNotFound' - -PW_HW_ADDRESS = "HwAddress" -PW_PERM_HW_ADDRESS = "PermHwAddress" -PW_MODE = "Mode" -PW_BITRATE = "Bitrate" -PW_ACCESS_POINTS = "AccessPoints" -PW_ACTIVE_ACCESS_POINT = "ActiveAccessPoint" -PW_WIRELESS_CAPABILITIES = "WirelessCapabilities" +PRP_WIFI_HW_ADDRESS = "HwAddress" +PRP_WIFI_PERM_HW_ADDRESS = "PermHwAddress" +PRP_WIFI_MODE = "Mode" +PRP_WIFI_BITRATE = "Bitrate" +PRP_WIFI_ACCESS_POINTS = "AccessPoints" +PRP_WIFI_ACTIVE_ACCESS_POINT = "ActiveAccessPoint" +PRP_WIFI_WIRELESS_CAPABILITIES = "WirelessCapabilities" +PRP_WIFI_LAST_SCAN = "LastScan" class WifiDevice(Device): - def __init__(self, bus, iface): - self.mac = random_mac() - self.aps = [] - self.active_ap = None + def __init__(self, iface, mac = None, ident = None): + Device.__init__(self, iface, NM.DeviceType.WIFI, ident) - self.add_dbus_interface(IFACE_WIFI, self.__get_props, WifiDevice.PropertiesChanged) - Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIFI) + if mac is None: + mac = Util.random_mac(self.ident) + + self.aps = [] + self.scan_cb_id = None + + # Note: we would like to simulate how nmcli calls RequestScan() and we could + # do so by using an older timestamp. However, that makes the client tests + # racy, because if a bunch of nmcli instances run in parallel against this + # service, earlier instances will issue a RequestScan(), while later instances + # won't do that (because the LastScan timestamp is already updated). That means, + # the later instances will print the scan result immediately, and in another sort + # order. That should be fixed, by nmcli not starting to print anything, before + # all RequestScan() requests complete, and thus, always print a consistent list + # of results. + ts = NM.utils_get_timestamp_msec() + + props = { + PRP_WIFI_HW_ADDRESS: mac, + PRP_WIFI_PERM_HW_ADDRESS: mac, + PRP_WIFI_MODE: dbus.UInt32(getattr(NM,'80211Mode').INFRA), + PRP_WIFI_BITRATE: dbus.UInt32(21000), + PRP_WIFI_WIRELESS_CAPABILITIES: dbus.UInt32(0xFF), + PRP_WIFI_ACCESS_POINTS: ExportedObj.to_path_array(self.aps), + PRP_WIFI_ACTIVE_ACCESS_POINT: ExportedObj.to_path(None), + PRP_WIFI_LAST_SCAN: dbus.Int64(ts), + } + + self.dbus_interface_add(IFACE_WIFI, props, WifiDevice.PropertiesChanged) - # methods @dbus.service.method(dbus_interface=IFACE_WIFI, in_signature='', out_signature='ao') def GetAccessPoints(self): # only include non-hidden APs - return to_path_array([a for a in self.aps if a.ssid]) + return ExportedObj.to_path_array([a for a in self.aps if a.ssid]) @dbus.service.method(dbus_interface=IFACE_WIFI, in_signature='', out_signature='ao') def GetAllAccessPoints(self): # include all APs including hidden ones - return to_path_array(self.aps) + return ExportedObj.to_path_array(self.aps) @dbus.service.method(dbus_interface=IFACE_WIFI, in_signature='a{sv}', out_signature='') def RequestScan(self, props): + self.scan_cb_id = Util.g_source_remove(self.scan_cb_id) + def cb(): + ts = NM.utils_get_timestamp_msec() + for ap in self.aps: + ap._dbus_property_set(IFACE_WIFI_AP, PRP_WIFI_AP_LAST_SEEN, dbus.Int32(ts / 1000)) + self._dbus_property_set(IFACE_WIFI, PRP_WIFI_LAST_SCAN, dbus.Int64(ts)) + self.scan_cb_id = None + return False + self.scan_cb_id = GLib.idle_add(cb) pass @dbus.service.signal(IFACE_WIFI, signature='o') @@ -408,170 +963,130 @@ class WifiDevice(Device): pass def add_ap(self, ap): + ap.export() self.aps.append(ap) - self.__notify(PW_ACCESS_POINTS) - self.AccessPointAdded(to_path(ap)) + self._dbus_property_set(IFACE_WIFI, PRP_WIFI_ACCESS_POINTS, ExportedObj.to_path_array(self.aps)) + self.AccessPointAdded(ExportedObj.to_path(ap)) + return ap + + def remove_ap(self, ap): + self.aps.remove(ap) + self._dbus_property_set(IFACE_WIFI, PRP_WIFI_ACCESS_POINTS, ExportedObj.to_path_array(self.aps)) + self.AccessPointRemoved(ExportedObj.to_path(ap)) + ap.unexport() + + def stop(self): + self.scan_cb_id = Util.g_source_remove(self.scan_cb_id) + super(WifiDevice, self).stop() @dbus.service.signal(IFACE_WIFI, signature='o') def AccessPointRemoved(self, ap_path): pass - def remove_ap(self, ap): - self.aps.remove(ap) - self.__notify(PW_ACCESS_POINTS) - self.AccessPointRemoved(to_path(ap)) - - # Properties interface - def __get_props(self): - props = {} - props[PW_HW_ADDRESS] = self.mac - props[PW_PERM_HW_ADDRESS] = self.mac - props[PW_MODE] = dbus.UInt32(3) # NM_802_11_MODE_INFRA - props[PW_BITRATE] = dbus.UInt32(21000) - props[PW_WIRELESS_CAPABILITIES] = dbus.UInt32(0xFF) - props[PW_ACCESS_POINTS] = to_path_array(self.aps) - props[PW_ACTIVE_ACCESS_POINT] = to_path(self.active_ap) - return props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_WIFI, propname) - @dbus.service.signal(IFACE_WIFI, signature='a{sv}') def PropertiesChanged(self, changed): pass - # test functions - def add_test_ap(self, ssid, mac): - ap = WifiAp(self._bus, ssid, mac, 0x1, 0x1cc, 0x1cc, 2412) - self.add_ap(ap) - return ap - def remove_ap_by_path(self, path): for ap in self.aps: if ap.path == path: self.remove_ap(ap) return - raise ApNotFoundException("AP %s not found" % path) + raise BusErr.ApNotFoundException("AP %s not found" % path) -################################################################### -IFACE_WIMAX_NSP = 'org.freedesktop.NetworkManager.WiMax.Nsp' +############################################################################### -PN_NAME = "Name" -PN_SIGNAL_QUALITY = "SignalQuality" -PN_NETWORK_TYPE = "NetworkType" +PRP_WIMAX_NSP_NAME = "Name" +PRP_WIMAX_NSP_SIGNAL_QUALITY = "SignalQuality" +PRP_WIMAX_NSP_NETWORK_TYPE = "NetworkType" class WimaxNsp(ExportedObj): - counter = 0 - def __init__(self, bus, name): - path = "/org/freedesktop/NetworkManager/Nsp/%d" % WimaxNsp.counter - WimaxNsp.counter = WimaxNsp.counter + 1 + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/Nsp/" - self.name = name - self.strength = random.randint(0, 100) - self.strength_id = GLib.timeout_add_seconds(10, self.strength_cb, None) + def __init__(self, name): - self.add_dbus_interface(IFACE_WIMAX_NSP, self.__get_props, WimaxNsp.PropertiesChanged) - ExportedObj.__init__(self, bus, path) + ExportedObj.__init__(self, ExportedObj.create_path(WimaxNsp)) - def __del__(self): - if self.strength_id > 0: - GLib.source_remove(self.strength_id) - self.strength_id = 0 + strength = Util.random_int(self.path, 100) - def strength_cb(self, ignored): - self.strength = random.randint(0, 100) - self.__notify(PN_SIGNAL_QUALITY) - return True + props = { + PRP_WIMAX_NSP_NAME: name, + PRP_WIMAX_NSP_SIGNAL_QUALITY: dbus.UInt32(strength), + PRP_WIMAX_NSP_NETWORK_TYPE: dbus.UInt32(NM.WimaxNspNetworkType.HOME), + } - # Properties interface - def __get_props(self): - props = {} - props[PN_NAME] = self.name - props[PN_SIGNAL_QUALITY] = dbus.UInt32(self.strength) - props[PN_NETWORK_TYPE] = dbus.UInt32(0x1) # NM_WIMAX_NSP_NETWORK_TYPE_HOME - return props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_WIMAX_NSP, propname) + self.dbus_interface_add(IFACE_WIMAX_NSP, props, WimaxNsp.PropertiesChanged) @dbus.service.signal(IFACE_WIMAX_NSP, signature='a{sv}') def PropertiesChanged(self, changed): pass -################################################################### -IFACE_WIMAX = 'org.freedesktop.NetworkManager.Device.WiMax' - -class NspNotFoundException(dbus.DBusException): - _dbus_error_name = IFACE_WIMAX + '.NspNotFound' +############################################################################### -PX_NSPS = "Nsps" -PX_HW_ADDRESS = "HwAddress" -PX_CENTER_FREQUENCY = "CenterFrequency" -PX_RSSI = "Rssi" -PX_CINR = "Cinr" -PX_TX_POWER = "TxPower" -PX_BSID = "Bsid" -PX_ACTIVE_NSP = "ActiveNsp" +PRP_WIMAX_NSPS = "Nsps" +PRP_WIMAX_HW_ADDRESS = "HwAddress" +PRP_WIMAX_CENTER_FREQUENCY = "CenterFrequency" +PRP_WIMAX_RSSI = "Rssi" +PRP_WIMAX_CINR = "Cinr" +PRP_WIMAX_TX_POWER = "TxPower" +PRP_WIMAX_BSID = "Bsid" +PRP_WIMAX_ACTIVE_NSP = "ActiveNsp" class WimaxDevice(Device): - def __init__(self, bus, iface): - self.mac = random_mac() - self.bsid = random_mac() + def __init__(self, iface, ident = None): + Device.__init__(self, iface, NM.DeviceType.WIMAX, ident) + + mac = Util.random_mac(self.ident) + bsid = Util.random_mac(self.ident + '.bsid') + self.nsps = [] - self.active_nsp = None - self.add_dbus_interface(IFACE_WIMAX, self.__get_props, WimaxDevice.PropertiesChanged) - Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIMAX) + props = { + PRP_WIMAX_HW_ADDRESS: mac, + PRP_WIMAX_CENTER_FREQUENCY: dbus.UInt32(2525), + PRP_WIMAX_RSSI: dbus.Int32(-48), + PRP_WIMAX_CINR: dbus.Int32(24), + PRP_WIMAX_TX_POWER: dbus.Int32(9), + PRP_WIMAX_BSID: bsid, + PRP_WIMAX_NSPS: ExportedObj.to_path_array(self.nsps), + PRP_WIMAX_ACTIVE_NSP: ExportedObj.to_path(None), + } + + self.dbus_interface_add(IFACE_WIMAX, props, WimaxDevice.PropertiesChanged) - # methods @dbus.service.method(dbus_interface=IFACE_WIMAX, in_signature='', out_signature='ao') def GetNspList(self): - # include all APs including hidden ones - return to_path_array(self.nsps) + return ExportedObj.to_path_array(self.nsps) @dbus.service.signal(IFACE_WIMAX, signature='o') def NspAdded(self, nsp_path): pass def add_nsp(self, nsp): + nsp.export() self.nsps.append(nsp) - self.__notify(PX_NSPS) - self.NspAdded(to_path(nsp)) + self._dbus_property_set(IFACE_WIMAX, PRP_WIMAX_NSPS, ExportedObj.to_path_array(self.nsps)) + self.NspAdded(ExportedObj.to_path(nsp)) + + def remove_nsp(self, nsp): + self.nsps.remove(nsp) + self._dbus_property_set(IFACE_WIMAX, PRP_WIMAX_NSPS, ExportedObj.to_path_array(self.nsps)) + self.NspRemoved(ExportedObj.to_path(nsp)) + nsp.unexport() @dbus.service.signal(IFACE_WIMAX, signature='o') def NspRemoved(self, nsp_path): pass - def remove_nsp(self, nsp): - self.nsps.remove(nsp) - self.__notify(PX_NSPS) - self.NspRemoved(to_path(nsp)) - - # Properties interface - def __get_props(self): - props = {} - props[PX_HW_ADDRESS] = self.mac - props[PX_CENTER_FREQUENCY] = dbus.UInt32(2525) - props[PX_RSSI] = dbus.Int32(-48) - props[PX_CINR] = dbus.Int32(24) - props[PX_TX_POWER] = dbus.Int32(9) - props[PX_BSID] = self.bsid - props[PX_NSPS] = to_path_array(self.nsps) - props[PX_ACTIVE_NSP] = to_path(self.active_nsp) - return props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_WIMAX, propname) - @dbus.service.signal(IFACE_WIMAX, signature='a{sv}') def PropertiesChanged(self, changed): pass - # test functions def add_test_nsp(self, name): - nsp = WimaxNsp(self._bus, name) + nsp = WimaxNsp(name) self.add_nsp(nsp) return nsp @@ -580,206 +1095,257 @@ class WimaxDevice(Device): if nsp.path == path: self.remove_nsp(nsp) return - raise NspNotFoundException("NSP %s not found" % path) + raise BusErr.NspNotFoundException("NSP %s not found" % path) + +############################################################################### + +PRP_ACTIVE_CONNECTION_CONNECTION = "Connection" +PRP_ACTIVE_CONNECTION_SPECIFIC_OBJECT = "SpecificObject" +PRP_ACTIVE_CONNECTION_ID = "Id" +PRP_ACTIVE_CONNECTION_UUID = "Uuid" +PRP_ACTIVE_CONNECTION_TYPE = "Type" +PRP_ACTIVE_CONNECTION_DEVICES = "Devices" +PRP_ACTIVE_CONNECTION_STATE = "State" +PRP_ACTIVE_CONNECTION_DEFAULT = "Default" +PRP_ACTIVE_CONNECTION_IP4CONFIG = "Ip4Config" +PRP_ACTIVE_CONNECTION_DHCP4CONFIG = "Dhcp4Config" +PRP_ACTIVE_CONNECTION_DEFAULT6 = "Default6" +PRP_ACTIVE_CONNECTION_IP6CONFIG = "Ip6Config" +PRP_ACTIVE_CONNECTION_DHCP6CONFIG = "Dhcp6Config" +PRP_ACTIVE_CONNECTION_VPN = "Vpn" +PRP_ACTIVE_CONNECTION_MASTER = "Master" + +PRP_VPN_CONNECTION_VPN_STATE = 'VpnState' +PRP_VPN_CONNECTION_BANNER = 'Banner' -################################################################### -IFACE_ACTIVE_CONNECTION = 'org.freedesktop.NetworkManager.Connection.Active' +class ActiveConnection(ExportedObj): -PAC_CONNECTION = "Connection" -PAC_SPECIFIC_OBJECT = "SpecificObject" -PAC_ID = "Id" -PAC_UUID = "Uuid" -PAC_TYPE = "Type" -PAC_DEVICES = "Devices" -PAC_STATE = "State" -PAC_DEFAULT = "Default" -PAC_IP4CONFIG = "Ip4Config" -PAC_DHCP4CONFIG = "Dhcp4Config" -PAC_DEFAULT6 = "Default6" -PAC_IP6CONFIG = "Ip6Config" -PAC_DHCP6CONFIG = "Dhcp6Config" -PAC_VPN = "Vpn" -PAC_MASTER = "Master" + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/ActiveConnection/" -class ActiveConnection(ExportedObj): - counter = 1 + def __init__(self, device, con_inst, specific_object): - def __init__(self, bus, device, connection, specific_object): - object_path = "/org/freedesktop/NetworkManager/ActiveConnection/%d" % ActiveConnection.counter - ActiveConnection.counter = ActiveConnection.counter + 1 + ExportedObj.__init__(self, ExportedObj.create_path(ActiveConnection)) self.device = device - self.conn = connection - self.specific_object = specific_object - self.state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN - self.default = False - self.ip4config = None - self.dhcp4config = None - self.default6 = False - self.ip6config = None - self.dhcp6config = None - self.vpn = False - self.master = None - - self.add_dbus_interface(IFACE_ACTIVE_CONNECTION, self.__get_props, ActiveConnection.PropertiesChanged) - ExportedObj.__init__(self, bus, object_path) - - # Properties interface - def __get_props(self): - props = {} - props[PAC_CONNECTION] = to_path(self.conn) - props[PAC_SPECIFIC_OBJECT] = to_path(self.specific_object) - conn_settings = self.conn.GetSettings() - s_con = conn_settings['connection'] - props[PAC_ID] = s_con['id'] - props[PAC_UUID] = s_con['uuid'] - props[PAC_TYPE] = s_con['type'] - props[PAC_DEVICES] = to_path_array([self.device]) - props[PAC_STATE] = dbus.UInt32(self.state) - props[PAC_DEFAULT] = self.default - props[PAC_IP4CONFIG] = to_path(self.ip4config) - props[PAC_DHCP4CONFIG] = to_path(self.dhcp4config) - props[PAC_DEFAULT6] = self.default6 - props[PAC_IP6CONFIG] = to_path(self.ip6config) - props[PAC_DHCP6CONFIG] = to_path(self.dhcp6config) - props[PAC_VPN] = self.vpn - props[PAC_MASTER] = to_path(self.master) - return props + self.con_inst = con_inst + self.is_vpn = con_inst.is_vpn() + + self._activation_id = None + + s_con = con_inst.con_hash[NM.SETTING_CONNECTION_SETTING_NAME] + + props = { + PRP_ACTIVE_CONNECTION_CONNECTION: ExportedObj.to_path(con_inst), + PRP_ACTIVE_CONNECTION_SPECIFIC_OBJECT: ExportedObj.to_path(specific_object), + PRP_ACTIVE_CONNECTION_ID: s_con[NM.SETTING_CONNECTION_ID], + PRP_ACTIVE_CONNECTION_UUID: s_con[NM.SETTING_CONNECTION_UUID], + PRP_ACTIVE_CONNECTION_TYPE: s_con[NM.SETTING_CONNECTION_TYPE], + PRP_ACTIVE_CONNECTION_DEVICES: ExportedObj.to_path_array([self.device]), + PRP_ACTIVE_CONNECTION_STATE: dbus.UInt32(NM.ActiveConnectionState.UNKNOWN), + PRP_ACTIVE_CONNECTION_DEFAULT: False, + PRP_ACTIVE_CONNECTION_IP4CONFIG: ExportedObj.to_path(None), + PRP_ACTIVE_CONNECTION_DHCP4CONFIG: ExportedObj.to_path(None), + PRP_ACTIVE_CONNECTION_DEFAULT6: False, + PRP_ACTIVE_CONNECTION_IP6CONFIG: ExportedObj.to_path(None), + PRP_ACTIVE_CONNECTION_DHCP6CONFIG: ExportedObj.to_path(None), + PRP_ACTIVE_CONNECTION_VPN: self.is_vpn, + PRP_ACTIVE_CONNECTION_MASTER: ExportedObj.to_path(None), + } + + self.dbus_interface_add(IFACE_ACTIVE_CONNECTION, props, ActiveConnection.PropertiesChanged) + + if self.is_vpn: + props = { + PRP_VPN_CONNECTION_VPN_STATE: dbus.UInt32(NM.VpnConnectionState.UNKNOWN), + PRP_VPN_CONNECTION_BANNER: '*** VPN connection %s ***' % (con_inst.get_id()), + } + + self.dbus_interface_add(IFACE_VPN_CONNECTION, props, ActiveConnection.VpnPropertiesChanged) + + + def _set_state(self, state, reason): + state = dbus.UInt32(state) + self._dbus_property_set(IFACE_ACTIVE_CONNECTION, PRP_ACTIVE_CONNECTION_STATE, state) + self.StateChanged(state, dbus.UInt32(reason)) + + def activation_cancel(self): + self._activation_id = Util.g_source_remove(self._activation_id) + + def _activation_step2(self): + assert self._activation_id is not None + self._activation_id = None + self._set_state(NM.ActiveConnectionState.ACTIVATED, NM.ActiveConnectionStateReason.UNKNOWN) + return False + + def _activation_step1(self): + assert self._activation_id is not None + self._activation_id = GLib.timeout_add(50, self._activation_step2) + self.device.set_active_connection(self) + self._set_state(NM.ActiveConnectionState.ACTIVATING, NM.ActiveConnectionStateReason.UNKNOWN) + return False + + def start_activation(self): + assert self._activation_id is None + self._activation_id = GLib.timeout_add(50, self._activation_step1) + + @dbus.service.signal(IFACE_VPN_CONNECTION, signature='a{sv}') + def PropertiesChanged(self, changed): + pass + VpnPropertiesChanged = PropertiesChanged @dbus.service.signal(IFACE_ACTIVE_CONNECTION, signature='a{sv}') def PropertiesChanged(self, changed): pass -################################################################### -IFACE_TEST = 'org.freedesktop.NetworkManager.LibnmGlibTest' -IFACE_NM = 'org.freedesktop.NetworkManager' - -class PermissionDeniedException(dbus.DBusException): - _dbus_error_name = IFACE_NM + '.PermissionDenied' - -class UnknownDeviceException(dbus.DBusException): - _dbus_error_name = IFACE_NM + '.UnknownDevice' - -class UnknownConnectionException(dbus.DBusException): - _dbus_error_name = IFACE_NM + '.UnknownConnection' - -PM_DEVICES = 'Devices' -PM_ALL_DEVICES = 'AllDevices' -PM_NETWORKING_ENABLED = 'NetworkingEnabled' -PM_WWAN_ENABLED = 'WwanEnabled' -PM_WWAN_HARDWARE_ENABLED = 'WwanHardwareEnabled' -PM_WIRELESS_ENABLED = 'WirelessEnabled' -PM_WIRELESS_HARDWARE_ENABLED = 'WirelessHardwareEnabled' -PM_WIMAX_ENABLED = 'WimaxEnabled' -PM_WIMAX_HARDWARE_ENABLED = 'WimaxHardwareEnabled' -PM_ACTIVE_CONNECTIONS = 'ActiveConnections' -PM_PRIMARY_CONNECTION = 'PrimaryConnection' -PM_ACTIVATING_CONNECTION = 'ActivatingConnection' -PM_STARTUP = 'Startup' -PM_STATE = 'State' -PM_VERSION = 'Version' -PM_CONNECTIVITY = 'Connectivity' - -def set_device_ac_cb(device, ac): - device.set_active_connection(ac) + @dbus.service.signal(IFACE_ACTIVE_CONNECTION, signature='uu') + def StateChanged(self, state, reason): + pass + + @dbus.service.signal(IFACE_VPN_CONNECTION, signature='uu') + def VpnStateChanged(self, state, reason): + pass + +############################################################################### + +PRP_NM_DEVICES = 'Devices' +PRP_NM_ALL_DEVICES = 'AllDevices' +PRP_NM_NETWORKING_ENABLED = 'NetworkingEnabled' +PRP_NM_WWAN_ENABLED = 'WwanEnabled' +PRP_NM_WWAN_HARDWARE_ENABLED = 'WwanHardwareEnabled' +PRP_NM_WIRELESS_ENABLED = 'WirelessEnabled' +PRP_NM_WIRELESS_HARDWARE_ENABLED = 'WirelessHardwareEnabled' +PRP_NM_WIMAX_ENABLED = 'WimaxEnabled' +PRP_NM_WIMAX_HARDWARE_ENABLED = 'WimaxHardwareEnabled' +PRP_NM_ACTIVE_CONNECTIONS = 'ActiveConnections' +PRP_NM_PRIMARY_CONNECTION = 'PrimaryConnection' +PRP_NM_ACTIVATING_CONNECTION = 'ActivatingConnection' +PRP_NM_STARTUP = 'Startup' +PRP_NM_STATE = 'State' +PRP_NM_VERSION = 'Version' +PRP_NM_CONNECTIVITY = 'Connectivity' class NetworkManager(ExportedObj): - def __init__(self, bus, object_path): - self._bus = bus; + def __init__(self): + ExportedObj.__init__(self, "/org/freedesktop/NetworkManager") self.devices = [] self.active_connections = [] - self.primary_connection = None - self.activating_connection = None - self.state = NM_STATE_DISCONNECTED - self.connectivity = 1 - self.add_dbus_interface(IFACE_NM, self.__get_props, NetworkManager.PropertiesChanged) - ExportedObj.__init__(self, bus, object_path) + props = { + PRP_NM_DEVICES: ExportedObj.to_path_array(self.devices), + PRP_NM_ALL_DEVICES: ExportedObj.to_path_array(self.devices), + PRP_NM_NETWORKING_ENABLED: True, + PRP_NM_WWAN_ENABLED: True, + PRP_NM_WWAN_HARDWARE_ENABLED: True, + PRP_NM_WIRELESS_ENABLED: True, + PRP_NM_WIRELESS_HARDWARE_ENABLED: True, + PRP_NM_WIMAX_ENABLED: True, + PRP_NM_WIMAX_HARDWARE_ENABLED: True, + PRP_NM_ACTIVE_CONNECTIONS: ExportedObj.to_path_array(self.active_connections), + PRP_NM_PRIMARY_CONNECTION: ExportedObj.to_path(None), + PRP_NM_ACTIVATING_CONNECTION: ExportedObj.to_path(None), + PRP_NM_STARTUP: False, + PRP_NM_STATE: dbus.UInt32(NM.State.DISCONNECTED), + PRP_NM_VERSION: "0.9.9.0", + PRP_NM_CONNECTIVITY: dbus.UInt32(NM.ConnectivityState.NONE), + } + + self.dbus_interface_add(IFACE_NM, props, NetworkManager.PropertiesChanged) + self.export() @dbus.service.signal(IFACE_NM, signature='u') def StateChanged(self, new_state): pass def set_state(self, new_state): - self.state = new_state - self.__notify(PM_STATE) + self._dbus_property_set(IFACE_NM, PRP_NM_STATE, state) self.StateChanged(dbus.UInt32(self.state)) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='', out_signature='ao') def GetDevices(self): - return to_path_array(self.devices) + return ExportedObj.to_path_array(self.devices) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='', out_signature='ao') def GetAllDevices(self): - return to_path_array(self.devices) + return ExportedObj.to_path_array(self.devices) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='s', out_signature='o') def GetDeviceByIpIface(self, ip_iface): - for d in self.devices: - # ignore iface/ip_iface distinction for now - if d.iface == ip_iface: - return to_path(d) - raise UnknownDeviceException("No device found for the requested iface.") + d = self.find_device_first(ip_iface = ip_iface, require = BusErr.UnknownDeviceException) + return ExportedObj.to_path(d) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='ooo', out_signature='o') def ActivateConnection(self, conpath, devpath, specific_object): try: - connection = settings.get_connection(conpath) + con_inst = gl.settings.get_connection(conpath) except Exception as e: - raise UnknownConnectionException("Connection not found") + raise BusErr.UnknownConnectionException("Connection not found") - hash = connection.GetSettings() - s_con = hash['connection'] + con_hash = con_inst.con_hash + con_type = NmUtil.con_hash_get_type(con_hash) + + device = self.find_device_first(path = devpath) + if not device: + if con_type == NM.SETTING_WIRED_SETTING_NAME: + device = self.find_device_first(dev_type = WiredDevice) + elif con_type == NM.SETTING_WIRELESS_SETTING_NAME: + device = self.find_device_first(dev_type = WifiDevice) + elif con_type == NM.SETTING_VLAN_SETTING_NAME: + ifname = con_hash[NM.SETTING_CONNECTION_SETTING_NAME]['interface-name'] + device = VlanDevice(ifname) + self.add_device(device) + elif con_type == NM.SETTING_VPN_SETTING_NAME: + for ac in self.active_connections: + if ac.is_vpn: + continue + if ac.device: + device = ac.device + break - device = None - for d in self.devices: - if d.path == devpath: - device = d - break - if not device and s_con['type'] == 'vlan': - ifname = s_con['interface-name'] - device = VlanDevice(self._bus, ifname) - self.add_device(device) if not device: - raise UnknownDeviceException("No device found for the requested iface.") + raise BusErr.UnknownDeviceException("No device found for the requested iface.") # See if we need secrets. For the moment, we only support WPA - if '802-11-wireless-security' in hash: - s_wsec = hash['802-11-wireless-security'] + if '802-11-wireless-security' in con_hash: + s_wsec = con_hash['802-11-wireless-security'] if (s_wsec['key-mgmt'] == 'wpa-psk' and 'psk' not in s_wsec): - secrets = agent_manager.get_secrets(hash, conpath, '802-11-wireless-security') + secrets = gl.agent_manager.get_secrets(con_hash, conpath, '802-11-wireless-security') if secrets is None: - raise NoSecretsException("No secret agent available") + raise BusErr.NoSecretsException("No secret agent available") if '802-11-wireless-security' not in secrets: - raise NoSecretsException("No secrets provided") + raise BusErr.NoSecretsException("No secrets provided") s_wsec = secrets['802-11-wireless-security'] if 'psk' not in s_wsec: - raise NoSecretsException("No secrets provided") + raise BusErr.NoSecretsException("No secrets provided") - ac = ActiveConnection(self._bus, device, connection, None) - self.active_connections.append(ac) - self.__notify(PM_ACTIVE_CONNECTIONS) + ac = ActiveConnection(device, con_inst, None) + self.active_connection_add(ac) - if s_con['id'] == 'object-creation-failed-test': - self.active_connections.remove(ac) - self.__notify(PM_ACTIVE_CONNECTIONS) - ac.remove_from_connection() - else: - GLib.timeout_add(50, set_device_ac_cb, device, ac) + if NmUtil.con_hash_get_id(con_hash) == 'object-creation-failed-test': + # FIXME: this is not the right test, to delete the active-connection + # before returning it. It's the wrong order of what NetworkManager + # would do. + self.active_connection_remove(ac) + return ExportedObj.to_path(ac) - return to_path(ac) + return ExportedObj.to_path(ac) - @dbus.service.method(dbus_interface=IFACE_NM, in_signature='a{sa{sv}}oo', out_signature='oo') - def AddAndActivateConnection(self, connection, devpath, specific_object): - device = None - for d in self.devices: - if d.path == devpath: - device = d - break - if not device: - raise UnknownDeviceException("No device found for the requested iface.") + def active_connection_add(self, ac): + ac.export() + self.active_connections.append(ac) + self._dbus_property_set(IFACE_NM, PRP_NM_ACTIVE_CONNECTIONS, ExportedObj.to_path_array(self.active_connections)) + ac.start_activation() - conpath = settings.AddConnection(connection) + def active_connection_remove(self, ac): + ac.activation_cancel() + self.active_connections.remove(ac) + self._dbus_property_set(IFACE_NM, PRP_NM_ACTIVE_CONNECTIONS, ExportedObj.to_path_array(self.active_connections)) + ac.unexport() + + @dbus.service.method(dbus_interface=IFACE_NM, in_signature='a{sa{sv}}oo', out_signature='oo') + def AddAndActivateConnection(self, con_hash, devpath, specific_object): + device = self.find_device_first(path = devpath, require = BusErr.UnknownDeviceException) + conpath = gl.settings.AddConnection(con_hash) return (conpath, self.ActivateConnection(conpath, devpath, specific_object)) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='o', out_signature='') @@ -789,10 +1355,10 @@ class NetworkManager(ExportedObj): @dbus.service.method(dbus_interface=IFACE_NM, in_signature='b', out_signature='') def Sleep(self, do_sleep): if do_sleep: - self.state = NM_STATE_ASLEEP + state = NM.State.ASLEEP else: - self.state = NM_STATE_DISCONNECTED - self.__notify(PM_STATE) + state = NM.State.DISCONNECTED + self.set_state(state) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='b', out_signature='') def Enable(self, do_enable): @@ -825,229 +1391,242 @@ class NetworkManager(ExportedObj): @dbus.service.method(dbus_interface=IFACE_NM, in_signature='', out_signature='u') def CheckConnectivity(self): - raise PermissionDeniedException("You fail") + raise BusErr.PermissionDeniedException("You fail") @dbus.service.signal(IFACE_NM, signature='o') def DeviceAdded(self, devpath): pass + def find_devices(self, ident = _DEFAULT_ARG, path = _DEFAULT_ARG, iface = _DEFAULT_ARG, ip_iface = _DEFAULT_ARG, dev_type = _DEFAULT_ARG): + r = None + for d in self.devices: + if ident is not _DEFAULT_ARG: + if d.ident != ident: + continue + if path is not _DEFAULT_ARG: + if d.path != path: + continue + if iface is not _DEFAULT_ARG: + if d.iface != iface: + continue + if ip_iface is not _DEFAULT_ARG: + # ignore iface/ip_iface distinction for now + if d.iface != ip_iface: + continue + if dev_type is not _DEFAULT_ARG: + if not isinstance(d, dev_type): + continue + yield d + + def find_device_first(self, ident = _DEFAULT_ARG, path = _DEFAULT_ARG, iface = _DEFAULT_ARG, ip_iface = _DEFAULT_ARG, dev_type = _DEFAULT_ARG, require = None): + r = None + for d in self.find_devices(ident = ident, path = path, iface = iface, ip_iface = ip_iface, dev_type = dev_type): + r = d + break + if r is None and require: + if require is TestError: + raise TestError('Device not found') + raise BusErr.UnknownDeviceException('Device not found') + return r + def add_device(self, device): + if self.find_device_first(ident = device.ident, path = device.path) is not None: + raise TestError("Duplicate device ident=%s / path=%s" % (device.ident, device.path)) + device.export() self.devices.append(device) - self.__notify(PM_DEVICES) - self.__notify(PM_ALL_DEVICES) - self.DeviceAdded(to_path(device)) + self._dbus_property_set(IFACE_NM, PRP_NM_DEVICES, ExportedObj.to_path_array(self.devices)) + self._dbus_property_set(IFACE_NM, PRP_NM_ALL_DEVICES, ExportedObj.to_path_array(self.devices)) + self.DeviceAdded(ExportedObj.to_path(device)) + device.start() + return device + + def remove_device(self, device): + device.stop() + self.devices.remove(device) + self._dbus_property_set(IFACE_NM, PRP_NM_DEVICES, ExportedObj.to_path_array(self.devices)) + self._dbus_property_set(IFACE_NM, PRP_NM_ALL_DEVICES, ExportedObj.to_path_array(self.devices)) + self.DeviceRemoved(ExportedObj.to_path(device)) + device.unexport() + + def devices_available_connections_update(self): + for d in self.devices: + d.available_connections_update() @dbus.service.signal(IFACE_NM, signature='o') def DeviceRemoved(self, devpath): pass - def remove_device(self, device): - self.devices.remove(device) - self.__notify(PM_DEVICES) - self.__notify(PM_ALL_DEVICES) - self.DeviceRemoved(to_path(device)) - - ################# D-Bus Properties interface - def __get_props(self): - props = {} - props[PM_DEVICES] = to_path_array(self.devices) - props[PM_ALL_DEVICES] = to_path_array(self.devices) - props[PM_NETWORKING_ENABLED] = True - props[PM_WWAN_ENABLED] = True - props[PM_WWAN_HARDWARE_ENABLED] = True - props[PM_WIRELESS_ENABLED] = True - props[PM_WIRELESS_HARDWARE_ENABLED] = True - props[PM_WIMAX_ENABLED] = True - props[PM_WIMAX_HARDWARE_ENABLED] = True - props[PM_ACTIVE_CONNECTIONS] = to_path_array(self.active_connections) - props[PM_PRIMARY_CONNECTION] = to_path(self.primary_connection) - props[PM_ACTIVATING_CONNECTION] = to_path(self.activating_connection) - props[PM_STARTUP] = False - props[PM_STATE] = dbus.UInt32(self.state) - props[PM_VERSION] = "0.9.9.0" - props[PM_CONNECTIVITY] = dbus.UInt32(self.connectivity) - return props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_NM, propname) - @dbus.service.signal(IFACE_NM, signature='a{sv}') def PropertiesChanged(self, changed): pass - ################# Testing methods @dbus.service.method(IFACE_TEST, in_signature='', out_signature='') def Quit(self): - mainloop.quit() + gl.mainloop.quit() + + @dbus.service.method(IFACE_TEST, in_signature='a{ss}', out_signature='a(sss)') + def FindConnections(self, selector_args): + return [(c.path, c.get_uuid(), c.get_id()) for c in gl.settings.find_connections(**selector_args)] + + @dbus.service.method(IFACE_TEST, in_signature='a(oa(sa(sv)))', out_signature='') + def SetProperties(self, all_args): + for i in [0, 1]: + for path, iface_args in all_args: + o = gl.object_manager.find_object(path) + if o is None: + raise TestError("Object %s does not exist" % (path)) + for iface_name, args in iface_args: + for propname, value in args: + o._dbus_property_set(iface_name, propname, value, + allow_detect_dbus_iface = True, + dry_run = (i == 0)) + + + @dbus.service.method(IFACE_TEST, in_signature='sa{sv}', out_signature='o') + def AddObj(self, class_name, args): + if class_name in ['WiredDevice', 'WifiDevice']: + py_class = globals()[class_name] + d = py_class(**args) + return ExportedObj.to_path(self.add_device(d)) + elif class_name in ['WifiAp']: + if 'device' not in args: + raise TestError('missing "device" paramter') + d = self.find_device_first(ident = args['device'], require = TestError) + del args['device'] + if 'ssid' not in args: + args['ssid'] = d.ident + '-ap-' + str(WifiAp.path_counter_next) + ap = WifiAp(**args) + return ExportedObj.to_path(d.add_ap(ap)) + raise TestError("Invalid python type \"%s\"" % (class_name)) @dbus.service.method(IFACE_TEST, in_signature='ssas', out_signature='o') def AddWiredDevice(self, ifname, mac, subchannels): - for d in self.devices: - if d.iface == ifname: - raise PermissionDeniedException("Device already added") - dev = WiredDevice(self._bus, ifname, mac, subchannels) - self.add_device(dev) - return to_path(dev) + dev = WiredDevice(ifname, mac, subchannels) + return ExportedObj.to_path(self.add_device(dev)) @dbus.service.method(IFACE_TEST, in_signature='s', out_signature='o') def AddWifiDevice(self, ifname): - for d in self.devices: - if d.iface == ifname: - raise PermissionDeniedException("Device already added") - dev = WifiDevice(self._bus, ifname) - self.add_device(dev) - return to_path(dev) + dev = WifiDevice(ifname) + return ExportedObj.to_path(self.add_device(dev)) @dbus.service.method(IFACE_TEST, in_signature='s', out_signature='o') def AddWimaxDevice(self, ifname): - for d in self.devices: - if d.iface == ifname: - raise PermissionDeniedException("Device already added") - dev = WimaxDevice(self._bus, ifname) - self.add_device(dev) - return to_path(dev) + dev = WimaxDevice(ifname) + return ExportedObj.to_path(self.add_device(dev)) @dbus.service.method(IFACE_TEST, in_signature='o', out_signature='') def RemoveDevice(self, path): - for d in self.devices: - if d.path == path: - self.remove_device(d) - return - raise UnknownDeviceException("Device not found") + d = self.find_device_first(path = path, require = TestError) + self.remove_device(d) @dbus.service.method(IFACE_TEST, in_signature='sss', out_signature='o') - def AddWifiAp(self, ifname, ssid, mac): - for d in self.devices: - if d.iface == ifname: - return to_path(d.add_test_ap(ssid, mac)) - raise UnknownDeviceException("Device not found") + def AddWifiAp(self, ident, ssid, bssid): + d = self.find_device_first(ident = ident, require = TestError) + ap = WifiAp(ssid, bssid) + return ExportedObj.to_path(d.add_ap(ap)) @dbus.service.method(IFACE_TEST, in_signature='so', out_signature='') - def RemoveWifiAp(self, ifname, ap_path): - for d in self.devices: - if d.iface == ifname: - d.remove_ap_by_path(ap_path) - return - raise UnknownDeviceException("Device not found") + def RemoveWifiAp(self, ident, ap_path): + d = self.find_device_first(ident = ident, require = TestError) + d.remove_ap_by_path(ap_path) @dbus.service.method(IFACE_TEST, in_signature='ss', out_signature='o') - def AddWimaxNsp(self, ifname, name): - for d in self.devices: - if d.iface == ifname: - return to_path(d.add_test_nsp(name)) - raise UnknownDeviceException("Device not found") + def AddWimaxNsp(self, ident, name): + d = self.find_device_first(ident = ident, require = TestError) + return ExportedObj.to_path(d.add_test_nsp(name)) @dbus.service.method(IFACE_TEST, in_signature='so', out_signature='') - def RemoveWimaxNsp(self, ifname, nsp_path): - for d in self.devices: - if d.iface == ifname: - d.remove_nsp_by_path(nsp_path) - return - raise UnknownDeviceException("Device not found") + def RemoveWimaxNsp(self, ident, nsp_path): + d = self.find_device_first(ident = ident, require = TestError) + d.remove_nsp_by_path(nsp_path) @dbus.service.method(IFACE_TEST, in_signature='', out_signature='') def AutoRemoveNextConnection(self): - settings.auto_remove_next_connection() + gl.settings.auto_remove_next_connection() @dbus.service.method(dbus_interface=IFACE_TEST, in_signature='a{sa{sv}}b', out_signature='o') - def AddConnection(self, connection, verify_connection): - return settings.add_connection(connection, verify_connection) + def AddConnection(self, con_hash, do_verify_strict): + return gl.settings.add_connection(con_hash, do_verify_strict) @dbus.service.method(dbus_interface=IFACE_TEST, in_signature='sa{sa{sv}}b', out_signature='') - def UpdateConnection(self, path, connection, verify_connection): - return settings.update_connection(connection, path, verify_connection) + def UpdateConnection(self, path, con_hash, do_verify_strict): + return gl.settings.update_connection(con_hash, path, do_verify_strict) + + @dbus.service.method(dbus_interface=IFACE_TEST, in_signature='ba{ss}', out_signature='') + def ConnectionSetVisible(self, vis, selector_args): + cons = list(gl.settings.find_connections(**selector_args)) + assert(len(cons) == 1) + cons[0].SetVisible(vis) @dbus.service.method(dbus_interface=IFACE_TEST, in_signature='', out_signature='') def Restart(self): - bus.release_name("org.freedesktop.NetworkManager") - bus.request_name("org.freedesktop.NetworkManager") + gl.bus.release_name("org.freedesktop.NetworkManager") + gl.bus.request_name("org.freedesktop.NetworkManager") -################################################################### -IFACE_CONNECTION = 'org.freedesktop.NetworkManager.Settings.Connection' +############################################################################### -class InvalidPropertyException(dbus.DBusException): - _dbus_error_name = IFACE_CONNECTION + '.InvalidProperty' +PRP_CONNECTION_UNSAVED = 'Unsaved' +PRP_CONNECTION_FILENAME = 'Filename' -class MissingPropertyException(dbus.DBusException): - _dbus_error_name = IFACE_CONNECTION + '.MissingProperty' +class Connection(ExportedObj): + def __init__(self, path_counter, con_hash, do_verify_strict=True): -class InvalidSettingException(dbus.DBusException): - _dbus_error_name = IFACE_CONNECTION + '.InvalidSetting' + path = "/org/freedesktop/NetworkManager/Settings/Connection/%s" % (path_counter) -class MissingSettingException(dbus.DBusException): - _dbus_error_name = IFACE_CONNECTION + '.MissingSetting' + ExportedObj.__init__(self, path) -class Connection(ExportedObj): - def __init__(self, bus, object_path, settings, remove_func, verify_connection=True): + s_con = con_hash.get(NM.SETTING_CONNECTION_SETTING_NAME) + if s_con is None: + s_con = {} + con_hash[NM.SETTING_CONNECTION_SETTING_NAME] = s_con + if NmUtil.con_hash_get_id(con_hash) is None: + s_con[NM.SETTING_CONNECTION_ID] = 'connection-%s' % (path_counter) + if NmUtil.con_hash_get_uuid(con_hash) is None: + s_con[NM.SETTING_CONNECTION_UUID] = str(uuid.uuid3(uuid.NAMESPACE_URL, path)) - if self.get_uuid(settings) is None: - if 'connection' not in settings: - settings['connection'] = { } - settings['connection']['uuid'] = uuid.uuid4() - self.verify(settings, verify_strict=verify_connection) + NmUtil.con_hash_verify(con_hash, do_verify_strict=do_verify_strict) - self.path = object_path - self.settings = settings - self.remove_func = remove_func + self.path = path + self.con_hash = con_hash self.visible = True - self.props = {} - self.props['Unsaved'] = False - - self.add_dbus_interface(IFACE_CONNECTION, self.__get_props, None) - ExportedObj.__init__(self, bus, object_path) - - def get_uuid(self, settings=None): - if settings is None: - settings = self.settings - if 'connection' in settings: - s_con = settings['connection'] - if 'uuid' in s_con: - return s_con['uuid'] - return None - def verify(self, settings=None, verify_strict=True): - if settings is None: - settings = self.settings; - if 'connection' not in settings: - raise MissingSettingException('connection: setting is required') - s_con = settings['connection'] - if 'type' not in s_con: - raise MissingPropertyException('connection.type: property is required') - if 'uuid' not in s_con: - raise MissingPropertyException('connection.uuid: property is required') - if 'id' not in s_con: - raise MissingPropertyException('connection.id: property is required') - - if not verify_strict: - return; - t = s_con['type'] - if t not in ['802-3-ethernet', '802-11-wireless', 'vlan', 'wimax']: - raise InvalidPropertyException('connection.type: unsupported connection type "%s"' % (t)) + props = { + PRP_CONNECTION_UNSAVED: False, + PRP_CONNECTION_FILENAME: "/etc/NetworkManager/system-connections/" + self.get_id(), + } + + self.dbus_interface_add(IFACE_CONNECTION, props) + + def get_id(self): + return NmUtil.con_hash_get_id(self.con_hash) + + def get_uuid(self): + return NmUtil.con_hash_get_uuid(self.con_hash) - def update_connection(self, settings, verify_connection): - self.verify(settings, verify_strict=verify_connection) + def get_type(self): + return NmUtil.con_hash_get_type(self.con_hash) + + def is_vpn(self): + return self.get_type() == NM.SETTING_VPN_SETTING_NAME + + def update_connection(self, con_hash, do_verify_strict): + + NmUtil.con_hash_verify(con_hash, do_verify_strict = do_verify_strict) old_uuid = self.get_uuid() - new_uuid = self.get_uuid(settings) + new_uuid = NmUtil.con_hash_get_uuid(con_hash) if old_uuid != new_uuid: - raise InvalidPropertyException('connection.uuid: cannot change the uuid from %s to %s' % (old_uuid, new_uuid)) + raise BusErr.InvalidPropertyException('connection.uuid: cannot change the uuid from %s to %s' % (old_uuid, new_uuid)) - self.settings = settings; + self.con_hash = con_hash; self.Updated() - def __get_props(self): - return self.props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_CONNECTION, propname) - - # Connection methods @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='', out_signature='a{sa{sv}}') def GetSettings(self): if not self.visible: - raise PermissionDeniedException() - return self.settings + raise BusErr.PermissionDeniedException() + return self.con_hash @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='b', out_signature='') def SetVisible(self, vis): @@ -1056,13 +1635,16 @@ class Connection(ExportedObj): @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='', out_signature='') def Delete(self): - self.remove_func(self) - self.Removed() - self.remove_from_connection() + gl.settings.delete_connection(self) @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='a{sa{sv}}', out_signature='') - def Update(self, settings): - self.update_connection(settings, TRUE) + def Update(self, con_hash): + self.update_connection(con_hash, True) + + @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='a{sa{sv}}ua{sv}', out_signature='a{sv}') + def Update2(self, con_hash, flags, args): + self.update_connection(con_hash, True) + return [] @dbus.service.signal(IFACE_CONNECTION, signature='') def Removed(self): @@ -1072,25 +1654,28 @@ class Connection(ExportedObj): def Updated(self): pass -################################################################### -IFACE_SETTINGS = 'org.freedesktop.NetworkManager.Settings' +############################################################################### -class InvalidHostnameException(dbus.DBusException): - _dbus_error_name = IFACE_SETTINGS + '.InvalidHostname' +PRP_SETTINGS_HOSTNAME = 'Hostname' +PRP_SETTINGS_CAN_MODIFY = 'CanModify' +PRP_SETTINGS_CONNECTIONS = 'Connections' class Settings(ExportedObj): - def __init__(self, bus, object_path): + def __init__(self): + ExportedObj.__init__(self, "/org/freedesktop/NetworkManager/Settings") + self.connections = {} - self.bus = bus - self.counter = 1 + self.c_counter = 0 self.remove_next_connection = False - self.props = {} - self.props['Hostname'] = "foobar.baz" - self.props['CanModify'] = True - self.props['Connections'] = dbus.Array([], 'o') - self.add_dbus_interface(IFACE_SETTINGS, self.__get_props, Settings.PropertiesChanged) - ExportedObj.__init__(self, bus, object_path) + props = { + PRP_SETTINGS_HOSTNAME: "foobar.baz", + PRP_SETTINGS_CAN_MODIFY: True, + PRP_SETTINGS_CONNECTIONS: dbus.Array([], 'o'), + } + + self.dbus_interface_add(IFACE_SETTINGS, props, Settings.PropertiesChanged) + self.export() def auto_remove_next_connection(self): self.remove_next_connection = True; @@ -1098,60 +1683,76 @@ class Settings(ExportedObj): def get_connection(self, path): return self.connections[path] + def get_connections(self, stable_order = True): + cons = list(self.connections.values()) + if stable_order: + cons.sort(key = lambda c: (Util.random_int(c.get_id()), Util.random_int(c.path))) + return cons + + def get_connection_paths(self, stable_order = True): + return [c.path for c in self.get_connections(stable_order = stable_order)] + + def find_connections(self, path = None, con_id = None, con_uuid = None): + for c in self.get_connections(): + if path is not None: + if c.path != path: + continue + if con_id is not None: + if c.get_id() != con_id: + continue + if con_uuid is not None: + if c.get_uuid() != con_uuid: + continue + yield c + @dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='', out_signature='ao') def ListConnections(self): - return self.connections.keys() + return self.get_connection_paths() @dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='a{sa{sv}}', out_signature='o') - def AddConnection(self, settings): - return self.add_connection(settings) + def AddConnection(self, con_hash): + return self.add_connection(con_hash) - def add_connection(self, settings, verify_connection=True): - path = "/org/freedesktop/NetworkManager/Settings/Connection/{0}".format(self.counter) - con = Connection(self.bus, path, settings, self.delete_connection, verify_connection) + def add_connection(self, con_hash, do_verify_strict=True): + self.c_counter += 1 + con_inst = Connection(self.c_counter, con_hash, do_verify_strict) - uuid = con.get_uuid() - if uuid in [c.get_uuid() for c in self.connections.values()]: - raise InvalidSettingException('cannot add duplicate connection with uuid %s' % (uuid)) + uuid = con_inst.get_uuid() + if uuid in [c.get_uuid() for c in self.get_connections(stable_order = False)]: + raise BusErr.InvalidSettingException('cannot add duplicate connection with uuid %s' % (uuid)) - self.counter = self.counter + 1 - self.connections[path] = con - self.props['Connections'] = dbus.Array(self.connections.keys(), 'o') - self.NewConnection(path) - self.__notify('Connections') + con_inst.export() + self.connections[con_inst.path] = con_inst + self.NewConnection(con_inst.path) + self._dbus_property_set(IFACE_SETTINGS, PRP_SETTINGS_CONNECTIONS, dbus.Array(self.get_connection_paths(), 'o')) if self.remove_next_connection: self.remove_next_connection = False - self.connections[path].Delete() + self.delete_connection(con_inst) - return path + gl.manager.devices_available_connections_update() + + return con_inst.path - def update_connection(self, connection, path=None, verify_connection=True): - if path is None: - path = connection.path + def update_connection(self, con_hash, path=None, do_verify_strict=True): if path not in self.connections: - raise UnknownConnectionException('Connection not found') - con = self.connections[path] - con.update_connection(connection, verify_connection) + raise BusErr.UnknownConnectionException('Connection not found') + self.connections[path].update_connection(con_hash, do_verify_strict) - def delete_connection(self, connection): - del self.connections[connection.path] - self.props['Connections'] = dbus.Array(self.connections.keys(), 'o') - self.__notify('Connections') + def delete_connection(self, con_inst): + del self.connections[con_inst.path] + self._dbus_property_set(IFACE_SETTINGS, PRP_SETTINGS_CONNECTIONS, dbus.Array(self.get_connection_paths(), 'o')) + con_inst.Removed() + con_inst.unexport() + + gl.manager.devices_available_connections_update() @dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='s', out_signature='') def SaveHostname(self, hostname): # Arbitrary requirement to test error handling if hostname.find('.') == -1: - raise InvalidHostnameException() - self.props['Hostname'] = hostname - self.__notify('Hostname') - - def __get_props(self): - return self.props - - def __notify(self, propname): - self._dbus_property_notify(IFACE_SETTINGS, propname) + raise BusErr.InvalidHostnameException() + self._dbus_property_set(IFACE_SETTINGS, PRP_SETTINGS_HOSTNAME, hostname) @dbus.service.signal(IFACE_SETTINGS, signature='o') def NewConnection(self, path): @@ -1163,11 +1764,381 @@ class Settings(ExportedObj): @dbus.service.method(IFACE_SETTINGS, in_signature='', out_signature='') def Quit(self): - mainloop.quit() + gl.mainloop.quit() + +############################################################################### + +PRP_IP4_CONFIG_ADDRESSES = 'Addresses' +PRP_IP4_CONFIG_ADDRESSDATA = 'AddressData' +PRP_IP4_CONFIG_GATEWAY = 'Gateway' +PRP_IP4_CONFIG_ROUTES = 'Routes' +PRP_IP4_CONFIG_ROUTEDATA = 'RouteData' +PRP_IP4_CONFIG_NAMESERVERS = 'Nameservers' +PRP_IP4_CONFIG_DOMAINS = 'Domains' +PRP_IP4_CONFIG_SEARCHES = 'Searches' +PRP_IP4_CONFIG_DNSOPTIONS = 'DnsOptions' +PRP_IP4_CONFIG_DNSPRIORITY = 'DnsPriority' +PRP_IP4_CONFIG_WINSSERVERS = 'WinsServers' + +class IP4Config(ExportedObj): + + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/IP4Config/" + + def __init__(self, generate_seed = _DEFAULT_ARG): + ExportedObj.__init__(self, ExportedObj.create_path(IP4Config)) + + if generate_seed == _DEFAULT_ARG: + generate_seed = self.path + + props = self._props_generate(generate_seed) + self.dbus_interface_add(IFACE_IP4_CONFIG, props, IP4Config.PropertiesChanged) + self.export() + + def _props_generate(self, generate_seed): + seed = Util.RandomSeed.wrap(generate_seed) + + gateway = None + if seed: + if Util.random_bool(seed): + gateway = Util.random_ip(seed, net = '192.168.0.0/16')[0] + + addrs = [] + if seed: + for n in range(0, Util.random_int(seed, 4)): + a = { + 'addr': Util.random_ip(seed, net = '192.168.0.0/16')[0], + 'prefix': Util.random_int(seed, 17, 32), + 'gateway': gateway if n == 0 else None, + } + addrs.append(a) + + routes = [] + if seed: + for n in range(0, Util.random_int(seed, 4)): + a = { + 'dest': Util.random_ip(seed, net = '192.168.0.0/16')[0], + 'prefix': Util.random_int(seed, 17, 32), + 'next-hop': None if (Util.random_int(seed) % 3 == 0) else Util.random_ip(seed, net = '192.168.0.0/16')[0], + 'metric': -1 if (Util.random_int(seed) % 3 == 0) else Util.random_int(seed, 0, 0xFFFFFFFF), + } + routes.append(a) + + nameservers = [] + if seed: + nameservers = list([Util.random_ip(seed, net = '192.168.0.0/16')[0] for x in range(Util.random_int(seed, 4))]) + + names_selection = ['foo1.bar', 'foo2.bar', 'foo3.bar', 'foo4.bar', 'fo.o.bar', 'fo.x.y']; + + domains = [] + if seed: + domains = Util.random_subset(seed, ['dom4.' + s for s in names_selection]) + + searches = [] + if seed: + domains = Util.random_subset(seed, ['sear4.' + s for s in names_selection]) + + dnsoptions = [] + if seed: + dnsoptions = Util.random_subset(seed, ['dns4-opt1', 'dns4-opt2', 'dns4-opt3', 'dns4-opt4']) + + dnspriority = 0 + if seed: + dnspriority = Util.random_int(seed, -10000, 10000) + + winsservers = [] + if seed: + winsservers = list([Util.random_ip(seed, net = '192.168.0.0/16')[0] for x in range(Util.random_int(seed, 4))]) + + return { + PRP_IP4_CONFIG_ADDRESSES: dbus.Array([ + [ Util.ip4_addr_ne32(a['addr']), + a['prefix'], + Util.ip4_addr_ne32(a['gateway']) if a['gateway'] else 0 + ] for a in addrs + ], + 'au'), + PRP_IP4_CONFIG_ADDRESSDATA: dbus.Array([ + dbus.Dictionary(collections.OrderedDict( [ ('address', dbus.String(a['addr'])), + ('prefix', dbus.UInt32(a['prefix']))] + \ + ([ ('gateway', dbus.String(a['gateway'])) ] if a['gateway'] else [])), + 'sv') + for a in addrs + ], + 'a{sv}'), + PRP_IP4_CONFIG_GATEWAY: dbus.String(gateway) if gateway else "", + PRP_IP4_CONFIG_ROUTES: dbus.Array([ + [ Util.ip4_addr_ne32(a['dest']), + a['prefix'], + Util.ip4_addr_ne32(a['next-hop'] or '0.0.0.0'), + max(a['metric'], 0) + ] for a in routes + ], + 'au'), + PRP_IP4_CONFIG_ROUTEDATA: dbus.Array([ + dbus.Dictionary(collections.OrderedDict( [ ('dest', dbus.String(a['dest'])), + ('prefix', dbus.UInt32(a['prefix']))] + \ + ([ ('next-hop', dbus.String(a['next-hop'])) ] if a['next-hop'] else []) + \ + ([ ('metric', dbus.UInt32(a['metric'])) ] if a['metric'] != -1 else [])), + 'sv') + for a in routes + ], + 'a{sv}'), + PRP_IP4_CONFIG_NAMESERVERS: dbus.Array([dbus.UInt32(Util.ip4_addr_ne32(n)) for n in nameservers], 'u'), + PRP_IP4_CONFIG_DOMAINS: dbus.Array(domains, 's'), + PRP_IP4_CONFIG_SEARCHES: dbus.Array(searches, 's'), + PRP_IP4_CONFIG_DNSOPTIONS: dbus.Array(dnsoptions, 's'), + PRP_IP4_CONFIG_DNSPRIORITY: dbus.Int32(dnspriority), + PRP_IP4_CONFIG_WINSSERVERS: dbus.Array([dbus.UInt32(Util.ip4_addr_ne32(n)) for n in winsservers], 'u'), + } + + def props_regenerate(self, generate_seed): + props = self.generate_props(generate_seed) + for k,v in props.items(): + self._dbus_property_set(IFACE_IP4_CONFIG, k, v) + + @dbus.service.signal(IFACE_IP4_CONFIG, signature='a{sv}') + def PropertiesChanged(self, path): + pass + +############################################################################### + +PRP_IP6_CONFIG_ADDRESSES = "Addresses" +PRP_IP6_CONFIG_ADDRESSDATA = "AddressData" +PRP_IP6_CONFIG_GATEWAY = "Gateway" +PRP_IP6_CONFIG_ROUTES = "Routes" +PRP_IP6_CONFIG_ROUTEDATA = "RouteData" +PRP_IP6_CONFIG_NAMESERVERS = "Nameservers" +PRP_IP6_CONFIG_DOMAINS = "Domains" +PRP_IP6_CONFIG_SEARCHES = "Searches" +PRP_IP6_CONFIG_DNSOPTIONS = "DnsOptions" +PRP_IP6_CONFIG_DNSPRIORITY = "DnsPriority" + +class IP6Config(ExportedObj): + + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/IP6Config/" + + def __init__(self, generate_seed = _DEFAULT_ARG): + ExportedObj.__init__(self, ExportedObj.create_path(IP6Config)) + + if generate_seed == _DEFAULT_ARG: + generate_seed = self.path + + props = self._props_generate(generate_seed) + self.dbus_interface_add(IFACE_IP6_CONFIG, props, IP6Config.PropertiesChanged) + self.export() + + def _props_generate(self, generate_seed): + seed = Util.RandomSeed.wrap(generate_seed) + + gateway = None + if seed: + if Util.random_bool(seed): + gateway = Util.random_ip(seed, net = '2001:a::/64')[0] + + addrs = [] + if seed: + for n in range(0, Util.random_int(seed, 4)): + a = { + 'addr': Util.random_ip(seed, net = '2001:a::/64')[0], + 'prefix': Util.random_int(seed, 65, 128), + 'gateway': gateway if n == 0 else None, + } + addrs.append(a) + + routes = [] + if seed: + for n in range(0, Util.random_int(seed, 4)): + a = { + 'dest': Util.random_ip(seed, net = '2001:a::/64')[0], + 'prefix': Util.random_int(seed, 65, 128), + 'next-hop': None if (Util.random_int(seed) % 3 == 0) else Util.random_ip(seed, net = '2001:a::/64')[0], + 'metric': -1 if (Util.random_int(seed) % 3 == 0) else Util.random_int(seed, 0, 0xFFFFFFFF), + } + routes.append(a) + + nameservers = [] + if seed: + nameservers = list([Util.random_ip(seed, net = '2001:a::/64')[0] for x in range(Util.random_int(seed, 4))]) + + names_selection = ['foo1.bar', 'foo2.bar', 'foo3.bar', 'foo4.bar', 'fo.o.bar', 'fo.x.y']; + + domains = [] + if seed: + domains = Util.random_subset(seed, ['dom6.' + s for s in names_selection]) + + searches = [] + if seed: + domains = Util.random_subset(seed, ['sear6.' + s for s in names_selection]) + + dnsoptions = [] + if seed: + dnsoptions = Util.random_subset(seed, ['dns6-opt1', 'dns6-opt2', 'dns6-opt3', 'dns6-opt4']) + + dnspriority = 0 + if seed: + dnspriority = Util.random_int(seed, -10000, 10000) + + return { + PRP_IP6_CONFIG_ADDRESSES: dbus.Array([ + [ Util.ip6_addr_ay(a['addr']), + a['prefix'], + Util.ip6_addr_ay(a['gateway'] or '::') + ] for a in addrs + ], + '(ayuay)'), + PRP_IP6_CONFIG_ADDRESSDATA: dbus.Array([ + dbus.Dictionary(collections.OrderedDict( [ ('address', dbus.String(a['addr'])), + ('prefix', dbus.UInt32(a['prefix']))] + \ + ([ ('gateway', dbus.String(a['gateway'])) ] if a['gateway'] else [])), + 'sv') + for a in addrs + ], + 'a{sv}'), + PRP_IP6_CONFIG_GATEWAY: dbus.String(gateway) if gateway else "", + PRP_IP6_CONFIG_ROUTES: dbus.Array([ + [ Util.ip6_addr_ay(a['dest']), + a['prefix'], + Util.ip6_addr_ay(a['next-hop'] or '::'), + max(a['metric'], 0) + ] for a in routes + ], + '(ayuayu)'), + PRP_IP6_CONFIG_ROUTEDATA: dbus.Array([ + dbus.Dictionary(collections.OrderedDict( [ ('dest', dbus.String(a['dest'])), + ('prefix', dbus.UInt32(a['prefix']))] + \ + ([ ('next-hop', dbus.String(a['next-hop'])) ] if a['next-hop'] else []) + \ + ([ ('metric', dbus.UInt32(a['metric'])) ] if a['metric'] != -1 else [])), + 'sv') + for a in routes + ], + 'a{sv}'), + PRP_IP6_CONFIG_NAMESERVERS: dbus.Array([Util.ip6_addr_ay(n) for n in nameservers], 'ay'), + PRP_IP6_CONFIG_DOMAINS: dbus.Array(domains, 's'), + PRP_IP6_CONFIG_SEARCHES: dbus.Array(searches, 's'), + PRP_IP6_CONFIG_DNSOPTIONS: dbus.Array(dnsoptions, 's'), + PRP_IP6_CONFIG_DNSPRIORITY: dbus.Int32(dnspriority), + } + + def props_regenerate(self, generate_seed): + props = self.generate_props(generate_seed) + for k,v in props.items(): + self._dbus_property_set(IFACE_IP6_CONFIG, k, v) + + @dbus.service.signal(IFACE_IP6_CONFIG, signature='a{sv}') + def PropertiesChanged(self, path): + pass + +############################################################################### + +PRP_DHCP4_CONFIG_OPTIONS = 'Options' + +class Dhcp4Config(ExportedObj): + + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/DHCP4Config/" + + def __init__(self, generate_seed = _DEFAULT_ARG): + ExportedObj.__init__(self, ExportedObj.create_path(Dhcp4Config)) + + if generate_seed == _DEFAULT_ARG: + generate_seed = self.path + + props = self._props_generate(generate_seed) + self.dbus_interface_add(IFACE_DHCP4_CONFIG, props, Dhcp4Config.PropertiesChanged) + self.export() + + def _props_generate(self, generate_seed): + seed = Util.RandomSeed.wrap(generate_seed) + + options = [] + if seed: + options = Util.random_subset(seed, [('dhcp-4-opt-' + str(i), 'val-' + str(i)) for i in range(10)]) + + return { + PRP_DHCP4_CONFIG_OPTIONS: dbus.Dictionary(collections.OrderedDict(options), + 'sv') + } -################################################################### -IFACE_AGENT_MANAGER = 'org.freedesktop.NetworkManager.AgentManager' -IFACE_AGENT = 'org.freedesktop.NetworkManager.SecretAgent' + def props_regenerate(self, generate_seed): + props = self.generate_props(generate_seed) + for k,v in props.items(): + self._dbus_property_set(IFACE_DHCP4_CONFIG, k, v) + + @dbus.service.signal(IFACE_DHCP4_CONFIG, signature='a{sv}') + def PropertiesChanged(self, path): + pass + +############################################################################### + +PRP_DHCP6_CONFIG_OPTIONS = 'Options' + +class Dhcp6Config(ExportedObj): + + path_counter_next = 1 + path_prefix = "/org/freedesktop/NetworkManager/DHCP6Config/" + + def __init__(self, generate_seed = _DEFAULT_ARG): + ExportedObj.__init__(self, ExportedObj.create_path(Dhcp6Config)) + + if generate_seed == _DEFAULT_ARG: + generate_seed = self.path + + props = self._props_generate(generate_seed) + self.dbus_interface_add(IFACE_DHCP6_CONFIG, props, Dhcp6Config.PropertiesChanged) + self.export() + + def _props_generate(self, generate_seed): + seed = Util.RandomSeed.wrap(generate_seed) + + options = [] + if seed: + options = Util.random_subset(seed, [('dhcp-6-opt-' + str(i), 'val-' + str(i)) for i in range(10)]) + + return { + PRP_DHCP4_CONFIG_OPTIONS: dbus.Dictionary(collections.OrderedDict(options), + 'sv') + } + + def props_regenerate(self, generate_seed): + props = self.generate_props(generate_seed) + for k,v in props.items(): + self._dbus_property_set(IFACE_DHCP6_CONFIG, k, v) + + @dbus.service.signal(IFACE_DHCP6_CONFIG, signature='a{sv}') + def PropertiesChanged(self, path): + pass + +############################################################################### + +PRP_DNS_MANAGER_MODE = 'Mode' +PRP_DNS_MANAGER_RC_MANAGER = 'RcManager' +PRP_DNS_MANAGER_CONFIGURATION = 'Configuration' + +class DnsManager(ExportedObj): + def __init__(self): + ExportedObj.__init__(self, "/org/freedesktop/NetworkManager/DnsManager") + + props = { + PRP_DNS_MANAGER_MODE: "dnsmasq", + PRP_DNS_MANAGER_RC_MANAGER: "symlink", + PRP_DNS_MANAGER_CONFIGURATION: dbus.Array( + [ + dbus.Dictionary( + { + 'nameservers' : dbus.Array(['1.2.3.4', '5.6.7.8'], 's'), + 'priority' : dbus.Int32(100), + }, + 'sv') + ], + 'a{sv}'), + } + + self.dbus_interface_add(IFACE_DNS_MANAGER, props) + self.export() + +############################################################################### PATH_SECRET_AGENT = '/org/freedesktop/NetworkManager/SecretAgent' @@ -1175,17 +2146,10 @@ FLAG_ALLOW_INTERACTION = 0x1 FLAG_REQUEST_NEW = 0x2 FLAG_USER_REQUESTED = 0x4 -class NoSecretsException(dbus.DBusException): - _dbus_error_name = IFACE_AGENT_MANAGER + '.NoSecrets' - -class UserCanceledException(dbus.DBusException): - _dbus_error_name = IFACE_AGENT_MANAGER + '.UserCanceled' - class AgentManager(dbus.service.Object): - def __init__(self, bus, object_path): - dbus.service.Object.__init__(self, bus, object_path) + def __init__(self): + dbus.service.Object.__init__(self, gl.bus, "/org/freedesktop/NetworkManager/AgentManager") self.agents = {} - self.bus = bus @dbus.service.method(dbus_interface=IFACE_AGENT_MANAGER, in_signature='s', out_signature='', @@ -1197,7 +2161,7 @@ class AgentManager(dbus.service.Object): in_signature='su', out_signature='', sender_keyword='sender') def RegisterWithCapabilities(self, name, caps, sender=None): - self.agents[sender] = self.bus.get_object(sender, PATH_SECRET_AGENT) + self.agents[sender] = gl.bus.get_object(sender, PATH_SECRET_AGENT) @dbus.service.method(dbus_interface=IFACE_AGENT_MANAGER, in_signature='', out_signature='', @@ -1205,7 +2169,7 @@ class AgentManager(dbus.service.Object): def Unregister(self, sender=None): del self.agents[sender] - def get_secrets(self, connection, path, setting_name): + def get_secrets(self, con_hash, path, setting_name): if len(self.agents) == 0: return None @@ -1213,47 +2177,37 @@ class AgentManager(dbus.service.Object): for sender in self.agents: agent = self.agents[sender] try: - secrets = agent.GetSecrets(connection, path, setting_name, + secrets = agent.GetSecrets(con_hash, path, setting_name, dbus.Array([], 's'), FLAG_ALLOW_INTERACTION | FLAG_USER_REQUESTED, dbus_interface=IFACE_AGENT) break except dbus.DBusException as e: if e.get_dbus_name() == IFACE_AGENT + '.UserCanceled': - raise UserCanceledException('User canceled') + raise BusErr.UserCanceledException('User canceled') continue return secrets -################################################################### -IFACE_OBJECT_MANAGER = 'org.freedesktop.DBus.ObjectManager' - -PATH_OBJECT_MANAGER = '/org/freedesktop' +############################################################################### class ObjectManager(dbus.service.Object): - def __init__(self, bus, object_path): - dbus.service.Object.__init__(self, bus, object_path) + def __init__(self, object_path): + dbus.service.Object.__init__(self, gl.bus, object_path) self.objs = [] - self.bus = bus - @dbus.service.method(dbus_interface=IFACE_OBJECT_MANAGER, - in_signature='', out_signature='a{oa{sa{sv}}}', - sender_keyword='sender') - def GetManagedObjects(self, sender=None): - managed_objects = {} - for obj in self.objs: - name, ifaces = obj.get_managed_ifaces() - managed_objects[name] = ifaces - return managed_objects + def find_object(self, path): + for o in self.objs: + if path == o.path: + return o + return None def add_object(self, obj): self.objs.append(obj) - name, ifaces = obj.get_managed_ifaces() - self.InterfacesAdded(name, ifaces) + self.InterfacesAdded(obj.path, obj.get_managed_ifaces()) def remove_object(self, obj): self.objs.remove(obj) - name, ifaces = obj.get_managed_ifaces() - self.InterfacesRemoved(name, ifaces.keys()) + self.InterfacesRemoved(obj.path, obj.get_managed_ifaces().keys()) @dbus.service.signal(IFACE_OBJECT_MANAGER, signature='oa{sa{sv}}') def InterfacesAdded(self, name, ifaces): @@ -1263,79 +2217,52 @@ class ObjectManager(dbus.service.Object): def InterfacesRemoved(self, name, ifaces): pass -################################################################### -IFACE_DNS_MANAGER = 'org.freedesktop.NetworkManager.DnsManager' - -class DnsManager(ExportedObj): - def __init__(self, bus, object_path): - self.props = {} - self.props['Mode'] = "dnsmasq" - self.props['RcManager'] = "symlink" - self.props['Configuration'] = dbus.Array([ - dbus.Dictionary( - { 'nameservers' : dbus.Array(['1.2.3.4', '5.6.7.8'], 's'), - 'priority' : dbus.Int32(100) }, - 'sv') ], - 'a{sv}') - - self.add_dbus_interface(IFACE_DNS_MANAGER, self.__get_props, None) - ExportedObj.__init__(self, bus, object_path) - - @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, iface): - if iface != IFACE_DNS_MANAGER: - raise UnknownInterfaceException() - return self.props - - @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') - def Get(self, iface, name): - if iface != IFACE_DNS_MANAGER: - raise UnknownInterfaceException() - if not name in self.props.keys(): - raise UnknownPropertyException() - return self.props[name] - - def __get_props(self): - return self.props - -################################################################### -def stdin_cb(io, condition): - mainloop.quit() + @dbus.service.method(dbus_interface=IFACE_OBJECT_MANAGER, + in_signature='', out_signature='a{oa{sa{sv}}}', + sender_keyword='sender') + def GetManagedObjects(self, sender=None): + managed_objects = {} + for obj in self.objs: + managed_objects[obj.path] = obj.get_managed_ifaces() + return managed_objects -def quit_cb(user_data): - mainloop.quit() +############################################################################### def main(): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) random.seed() - global manager, settings, agent_manager, dns_manager, object_manager, bus + global gl + gl = Global() - bus = dbus.SessionBus() - object_manager = ObjectManager(bus, "/org/freedesktop") - manager = NetworkManager(bus, "/org/freedesktop/NetworkManager") - settings = Settings(bus, "/org/freedesktop/NetworkManager/Settings") - agent_manager = AgentManager(bus, "/org/freedesktop/NetworkManager/AgentManager") - dns_manager = DnsManager(bus, "/org/freedesktop/NetworkManager/DnsManager") + gl.mainloop = GLib.MainLoop() + gl.bus = dbus.SessionBus() - if not bus.request_name("org.freedesktop.NetworkManager"): - sys.exit(1) + gl.object_manager = ObjectManager('/org/freedesktop') + gl.manager = NetworkManager() + gl.settings = Settings() + gl.dns_manager = DnsManager() + gl.agent_manager = AgentManager() + + if not gl.bus.request_name("org.freedesktop.NetworkManager"): + raise AssertionError("Failure to request D-Bus name org.freedesktop.NetworkManager") # Watch stdin; if it closes, assume our parent has crashed, and exit - io = GLib.IOChannel(0) - io.add_watch(GLib.IOCondition.HUP, stdin_cb) + id1 = GLib.IOChannel(0).add_watch(GLib.IOCondition.HUP, + lambda io, condition: gl.mainloop.quit() or True) - # also quit after inactivity to ensure we don't stick around if the above fails somehow - GLib.timeout_add_seconds(20, quit_cb, None) + gl.mainloop.run() - try: - mainloop.run() - except Exception as e: - pass + GLib.source_remove(id1) + + gl.agent_manager.remove_from_connection() + gl.dns_manager.unexport() + gl.settings.unexport() + gl.manager.unexport() + gl.object_manager.remove_from_connection() sys.exit(0) if __name__ == '__main__': main() - |