diff options
Diffstat (limited to 'tools/test-networkmanager-service.py')
| -rwxr-xr-x | tools/test-networkmanager-service.py | 1268 |
1 files changed, 1034 insertions, 234 deletions
diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 8563aa19..ab63b60a 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -15,13 +15,15 @@ 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 ############################################################################### @@ -43,49 +45,470 @@ class TestError(AssertionError): ############################################################################### -IFACE_DBUS = 'org.freedesktop.DBus' +class Util: -class UnknownInterfaceException(dbus.DBusException): - _dbus_error_name = IFACE_DBUS + '.UnknownInterface' + PY3 = (sys.version_info[0] == 3) -class UnknownPropertyException(dbus.DBusException): - _dbus_error_name = IFACE_DBUS + '.UnknownProperty' + @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)) -class Util: + @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 pseudorandom_stream(seed, length = None): - seed = str(seed) + 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 - i = 0 while length is None or length > 0: if not v: - s = seed + str(i) + s = Util.RandomSeed.get(seed) s = s.encode('utf8') v = hashlib.sha256(s).hexdigest() - i += 1 yield int(v[0:2], 16) v = v[2:] if length is not None: length -= 1 @staticmethod - def pseudorandom_num(seed, v_end, v_start = 0): + 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 - for r in Util.pseudorandom_stream(seed): + 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_mac(seed = None): - if seed is None: - r = tuple([random.randint(0, 255) for x in range(6)]) + 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: - r = tuple(Util.pseudorandom_stream(seed, 6)) - return '%02X:%02X:%02X:%02X:%02X:%02X' % r + 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: + + 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 ############################################################################### @@ -103,11 +526,8 @@ class ExportedObj(dbus.service.Object): @staticmethod def to_path_array(src): - array = dbus.Array([], signature=dbus.Signature('o')) - if src is not None: - for o in src: - array.append(ExportedObj.to_path(o)) - return array + return dbus.Array([ExportedObj.to_path(o) for o in src] if src else [], + signature=dbus.Signature('o')) @staticmethod def to_path(src): @@ -145,7 +565,7 @@ class ExportedObj(dbus.service.Object): def _dbus_interface_get(self, dbus_iface): if dbus_iface not in self._dbus_ifaces: - raise UnknownInterfaceException() + raise BusErr.UnknownInterfaceException() return self._dbus_ifaces[dbus_iface] def _dbus_interface_get_property(self, dbus_interface, propname = None): @@ -153,14 +573,14 @@ class ExportedObj(dbus.service.Object): 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): + 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(): @@ -184,15 +604,27 @@ class ExportedObj(dbus.service.Object): if propname not in props: raise TestError("No property '%s' on '%s' on '%s'" % (propname, dbus_iface, self.path)) - if isinstance(self, ActiveConnection) \ - and dbus_iface == 'org.freedesktop.NetworkManager.Connection.Active' \ - and propname == 'State': - return - else: + 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) @@ -232,11 +664,6 @@ class ExportedObj(dbus.service.Object): ############################################################################### -IFACE_DEVICE = 'org.freedesktop.NetworkManager.Device' - -class NotSoftwareException(dbus.DBusException): - _dbus_error_name = IFACE_DEVICE + '.NotSoftware' - PRP_DEVICE_UDI = "Udi" PRP_DEVICE_IFACE = "Interface" PRP_DEVICE_DRIVER = "Driver" @@ -263,16 +690,21 @@ class Device(ExportedObj): ExportedObj.__init__(self, ExportedObj.create_path(Device), ident) + self.ip4_config = None + self.ip6_config = None + self.dhcp4_config = None + self.dhcp6_config = None + 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(None), - PRP_DEVICE_IP6_CONFIG: ExportedObj.to_path(None), - PRP_DEVICE_DHCP4_CONFIG: ExportedObj.to_path(None), - PRP_DEVICE_DHCP6_CONFIG: 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), @@ -281,6 +713,34 @@ class Device(ExportedObj): 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 @@ -288,7 +748,7 @@ 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 @dbus.service.signal(IFACE_DEVICE, signature='a{sv}') @@ -298,9 +758,25 @@ class Device(ExportedObj): def set_active_connection(self, ac): 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)] -IFACE_WIRED = 'org.freedesktop.NetworkManager.Device.Wired' + 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" @@ -333,8 +809,6 @@ class WiredDevice(Device): ############################################################################### -IFACE_VLAN = 'org.freedesktop.NetworkManager.Device.Vlan' - PRP_VLAN_HW_ADDRESS = "HwAddress" PRP_VLAN_CARRIER = "Carrier" PRP_VLAN_VLAN_ID = "VlanId" @@ -357,8 +831,6 @@ class VlanDevice(Device): ############################################################################### -IFACE_WIFI_AP = 'org.freedesktop.NetworkManager.AccessPoint' - PRP_WIFI_AP_FLAGS = "Flags" PRP_WIFI_AP_WPA_FLAGS = "WpaFlags" PRP_WIFI_AP_RSN_FLAGS = "RsnFlags" @@ -389,7 +861,7 @@ class WifiAp(ExportedObj): if bssid is None: bssid = Util.random_mac(self.path) if strength is None: - strength = Util.pseudorandom_num(self.path, 100) + strength = Util.random_int(self.path, 100) self.ssid = ssid self.strength_counter = 0 @@ -416,7 +888,7 @@ class WifiAp(ExportedObj): def strength_cb(self, ignored): self.strength_counter += 1 - strength = Util.pseudorandom_num(self.path + str(self.strength_counter), 100) + strength = Util.random_int(self.path + str(self.strength_counter), 100) self._dbus_property_set(IFACE_WIFI_AP, PRP_WIFI_AP_STRENGTH, strength) return True @@ -426,11 +898,6 @@ class WifiAp(ExportedObj): ############################################################################### -IFACE_WIFI = 'org.freedesktop.NetworkManager.Device.Wireless' - -class ApNotFoundException(dbus.DBusException): - _dbus_error_name = IFACE_WIFI + '.AccessPointNotFound' - PRP_WIFI_HW_ADDRESS = "HwAddress" PRP_WIFI_PERM_HW_ADDRESS = "PermHwAddress" PRP_WIFI_MODE = "Mode" @@ -438,6 +905,7 @@ 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, iface, mac = None, ident = None): @@ -456,6 +924,7 @@ class WifiDevice(Device): 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: NM.utils_get_timestamp_msec(), } self.dbus_interface_add(IFACE_WIFI, props, WifiDevice.PropertiesChanged) @@ -504,13 +973,11 @@ class WifiDevice(Device): 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' - PRP_WIMAX_NSP_NAME = "Name" PRP_WIMAX_NSP_SIGNAL_QUALITY = "SignalQuality" PRP_WIMAX_NSP_NETWORK_TYPE = "NetworkType" @@ -549,11 +1016,6 @@ class WimaxNsp(ExportedObj): ############################################################################### -IFACE_WIMAX = 'org.freedesktop.NetworkManager.Device.WiMax' - -class NspNotFoundException(dbus.DBusException): - _dbus_error_name = IFACE_WIMAX + '.NspNotFound' - PRP_WIMAX_NSPS = "Nsps" PRP_WIMAX_HW_ADDRESS = "HwAddress" PRP_WIMAX_CENTER_FREQUENCY = "CenterFrequency" @@ -623,12 +1085,10 @@ 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) ############################################################################### -IFACE_ACTIVE_CONNECTION = 'org.freedesktop.NetworkManager.Connection.Active' - PRP_ACTIVE_CONNECTION_CONNECTION = "Connection" PRP_ACTIVE_CONNECTION_SPECIFIC_OBJECT = "SpecificObject" PRP_ACTIVE_CONNECTION_ID = "Id" @@ -645,29 +1105,32 @@ 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' + class ActiveConnection(ExportedObj): path_counter_next = 1 path_prefix = "/org/freedesktop/NetworkManager/ActiveConnection/" - def __init__(self, device, connection, specific_object): + def __init__(self, device, con_inst, specific_object): ExportedObj.__init__(self, ExportedObj.create_path(ActiveConnection)) self.device = device - self.conn = connection + self.con_inst = con_inst + self.is_vpn = con_inst.is_vpn() self._activation_id = None - conn_settings = self.conn.GetSettings() - s_con = conn_settings['connection'] + s_con = con_inst.con_hash[NM.SETTING_CONNECTION_SETTING_NAME] props = { - PRP_ACTIVE_CONNECTION_CONNECTION: ExportedObj.to_path(self.conn), + 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['id'], - PRP_ACTIVE_CONNECTION_UUID: s_con['uuid'], - PRP_ACTIVE_CONNECTION_TYPE: s_con['type'], + 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, @@ -676,12 +1139,21 @@ class ActiveConnection(ExportedObj): 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: False, + 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) @@ -711,6 +1183,11 @@ class ActiveConnection(ExportedObj): 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 @@ -719,19 +1196,11 @@ class ActiveConnection(ExportedObj): def StateChanged(self, state, reason): 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' + @dbus.service.signal(IFACE_VPN_CONNECTION, signature='uu') + def VpnStateChanged(self, state, reason): + pass -class UnknownConnectionException(dbus.DBusException): - _dbus_error_name = IFACE_NM + '.UnknownConnection' +############################################################################### PRP_NM_DEVICES = 'Devices' PRP_NM_ALL_DEVICES = 'AllDevices' @@ -796,44 +1265,57 @@ class NetworkManager(ExportedObj): @dbus.service.method(dbus_interface=IFACE_NM, in_signature='s', out_signature='o') def GetDeviceByIpIface(self, ip_iface): - d = self.find_device_first(ip_iface = ip_iface, require = UnknownDeviceException) + 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 = gl.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 and s_con['type'] == 'vlan': - ifname = s_con['interface-name'] - device = VlanDevice(ifname) - self.add_device(device) if not device: - raise UnknownDeviceException("No device found for the requested iface.") + 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 + + if not device: + 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 = gl.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(device, connection, None) + ac = ActiveConnection(device, con_inst, None) self.active_connection_add(ac) - if s_con['id'] == 'object-creation-failed-test': + 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. @@ -855,9 +1337,9 @@ class NetworkManager(ExportedObj): ac.unexport() @dbus.service.method(dbus_interface=IFACE_NM, in_signature='a{sa{sv}}oo', out_signature='oo') - def AddAndActivateConnection(self, connection, devpath, specific_object): - device = self.find_device_first(path = devpath, require = UnknownDeviceException) - conpath = gl.settings.AddConnection(connection) + 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='') @@ -903,13 +1385,13 @@ 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): + 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: @@ -925,17 +1407,20 @@ class NetworkManager(ExportedObj): # 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, require = None): + 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): + 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 UnknownDeviceException('Device not found') + raise BusErr.UnknownDeviceException('Device not found') return r def add_device(self, device): @@ -946,15 +1431,21 @@ class NetworkManager(ExportedObj): 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 @@ -968,8 +1459,8 @@ class NetworkManager(ExportedObj): gl.mainloop.quit() @dbus.service.method(IFACE_TEST, in_signature='a{ss}', out_signature='a(sss)') - def FindConnections(self, args): - return [(c.path, c.get_uuid(), c.get_id()) for c in gl.settings.find_connections(**args)] + 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): @@ -1048,12 +1539,18 @@ class NetworkManager(ExportedObj): 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 gl.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 gl.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): @@ -1063,100 +1560,67 @@ class NetworkManager(ExportedObj): ############################################################################### -IFACE_CONNECTION = 'org.freedesktop.NetworkManager.Settings.Connection' - -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' - PRP_CONNECTION_UNSAVED = 'Unsaved' +PRP_CONNECTION_FILENAME = 'Filename' class Connection(ExportedObj): - def __init__(self, path_counter, settings, verify_connection=True): + def __init__(self, path_counter, con_hash, do_verify_strict=True): path = "/org/freedesktop/NetworkManager/Settings/Connection/%s" % (path_counter) ExportedObj.__init__(self, path) - if 'connection' not in settings: - settings['connection'] = { } - if self.get_id(settings) is None: - settings['connection']['id'] = 'connection-%s' % (path_counter) - if self.get_uuid(settings) is None: - settings['connection']['uuid'] = str(uuid.uuid3(uuid.NAMESPACE_URL, path)) - self.verify(settings, verify_strict=verify_connection) + 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)) + + NmUtil.con_hash_verify(con_hash, do_verify_strict=do_verify_strict) self.path = path - self.settings = settings + self.con_hash = con_hash self.visible = True 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, settings=None): - if settings is None: - settings = self.settings - if 'connection' in settings: - s_con = settings['connection'] - if 'id' in s_con: - return s_con['id'] - return None + def get_id(self): + return NmUtil.con_hash_get_id(self.con_hash) - 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 get_uuid(self): + return NmUtil.con_hash_get_uuid(self.con_hash) - 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)) + 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, settings, verify_connection): - self.verify(settings, verify_strict=verify_connection) + 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() @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): @@ -1168,12 +1632,12 @@ class Connection(ExportedObj): 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, settings, flags, args): - self.update_connection(settings, True) + def Update2(self, con_hash, flags, args): + self.update_connection(con_hash, True) return [] @dbus.service.signal(IFACE_CONNECTION, signature='') @@ -1186,11 +1650,6 @@ class Connection(ExportedObj): ############################################################################### -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' @@ -1218,8 +1677,17 @@ 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.connections.values(): + for c in self.get_connections(): if path is not None: if c.path != path: continue @@ -1233,50 +1701,51 @@ class Settings(ExportedObj): @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): + def add_connection(self, con_hash, do_verify_strict=True): self.c_counter += 1 - con = Connection(self.c_counter, settings, verify_connection) + 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)) - con.export() - self.connections[con.path] = con - self.NewConnection(con.path) - self._dbus_property_set(IFACE_SETTINGS, PRP_SETTINGS_CONNECTIONS, dbus.Array(self.connections.keys(), 'o')) + 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.delete_connection(con) + self.delete_connection(con_inst) - return con.path + gl.manager.devices_available_connections_update() - def update_connection(self, connection, path=None, verify_connection=True): - if path is None: - path = connection.path + return con_inst.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, 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() - def delete_connection(self, connection): - del self.connections[connection.path] - self._dbus_property_set(IFACE_SETTINGS, PRP_SETTINGS_CONNECTIONS, dbus.Array(self.connections.keys(), 'o')) - connection.Removed() - connection.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() + raise BusErr.InvalidHostnameException() self._dbus_property_set(IFACE_SETTINGS, PRP_SETTINGS_HOSTNAME, hostname) @dbus.service.signal(IFACE_SETTINGS, signature='o') @@ -1293,7 +1762,349 @@ class Settings(ExportedObj): ############################################################################### -IFACE_DNS_MANAGER = 'org.freedesktop.NetworkManager.DnsManager' +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') + } + + 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' @@ -1323,21 +2134,12 @@ class DnsManager(ExportedObj): ############################################################################### -IFACE_AGENT_MANAGER = 'org.freedesktop.NetworkManager.AgentManager' -IFACE_AGENT = 'org.freedesktop.NetworkManager.SecretAgent' - PATH_SECRET_AGENT = '/org/freedesktop/NetworkManager/SecretAgent' 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): dbus.service.Object.__init__(self, gl.bus, "/org/freedesktop/NetworkManager/AgentManager") @@ -1361,7 +2163,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 @@ -1369,21 +2171,19 @@ 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' - class ObjectManager(dbus.service.Object): def __init__(self, object_path): dbus.service.Object.__init__(self, gl.bus, object_path) |