diff --git a/.readthedocs.yml b/.readthedocs.yml index f5fcf5d..327f8d4 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,6 +1,17 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# Copyright (C) 2023 igo95862 +--- + version: 2 +build: + os: "ubuntu-22.04" + tools: + python: "3.9" + +sphinx: + configuration: "docs/conf.py" + python: - version: 3.8 - install: - - requirements: requirements.txt + install: + - requirements: docs/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index d507da0..8435887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +## 3.0.0 + +### Breaking changes + +All enums were revisisted and updated in accordance to NetworkManager documentation. + +Some enums and their fields were renamed: + +* `AccessPointCapabilities` -> `WifiAccessPointCapabilities` +* `WirelessCapabilities` -> `WifiCapabilities` +* `WpaSecurityFlags` -> `WifiAccessPointSecurityFlags` + * `P2P_*` -> `PAIR_*` + * `BROADCAST_*` -> `GROUP_*` + * `AUTH_*` -> `KEY_MGMT_*` +* `ConnectionState` -> `ActiveConnectionState` +* `ConnectionStateReason` -> `ActiveConnectionStateReason` +* `ConnectionFlags` -> `SettingsConnectionFlags` +* `ConnectionStateFlags` -> `ActivationStateFlags` +* `DeviceCapabilities` -> `DeviceCapabilitiesFlags` +* `BluetoothCapabilities` -> `BluetoothCapabilitiesFlags` +* `ModemCapabilities` -> `ModemCapabilitiesFlags` +* `SecretAgentCapabilities` -> `SecretAgentCapabilitiesFlags` +* `VpnState` -> `VpnServiceState` +* `VpnFailure` + * `LOGIN_FAILURE` -> `LOGIN_FAILED` + +New enums: + +* `NetworkManagerCapabilities` +* `WimaxNSPNetworkType` +* `SecretAgentGetSecretsFlags` +* `CheckpointCreateFlags` +* `CheckpointRollbackResult` +* `SettingsAddConnection2Flags` +* `SettingsUpdate2Flags` +* `DeviceReapplyFlags` +* `NetworkManagerReloadFlags` +* `RadioFlags` +* `MptcpFlags` +* `VpnConnectionState` +* `VpnConnectionStateReason` + ## 2.0.0 ### Warning if you used pre-release version diff --git a/README.md b/README.md index 0522dbb..111a641 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Implements most NetworkManager dbus interfaces and objects. * `python-sdbus` version higher than 0.8rc2 -See [python-sdbus requirements](https://github.com/igo95862/python-sdbus#requirements). +See [python-sdbus requirements](https://github.com/python-sdbus/python-sdbus#requirements). ## Installation @@ -17,6 +17,8 @@ See [python-sdbus requirements](https://github.com/igo95862/python-sdbus#require # [Documentation](https://python-sdbus-networkmanager.readthedocs.io/en/latest/) -This is the sub-project of [python-sdbus](https://github.com/igo95862/python-sdbus). +See [this quickstart guide for brief introduction to NetworkManager D-Bus API](https://python-sdbus-networkmanager.readthedocs.io/en/latest/quickstart.html). + +This is the sub-project of [python-sdbus](https://github.com/python-sdbus/python-sdbus). See the [python-sdbus documentation](https://python-sdbus.readthedocs.io/en/latest/). diff --git a/docs/conf.py b/docs/conf.py index 62ae238..7c1a777 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# Copyright (C) 2020, 2021 igo95862 +# Copyright (C) 2020-2023 igo95862 # This file is part of python-sdbus @@ -24,6 +24,7 @@ author = 'igo95862' source_suffix = '.rst' extensions = ['sdbus.autodoc'] +html_theme = "sphinx_rtd_theme" autoclass_content = 'both' autodoc_typehints = 'description' diff --git a/docs/enums.rst b/docs/enums.rst index 946f685..10a5d60 100644 --- a/docs/enums.rst +++ b/docs/enums.rst @@ -1,74 +1,44 @@ Enums ================ -.. autoclass:: sdbus_async.networkmanager.AccessPointCapabilities - :members: +Python's Enum quick intro +------------------------- -.. autoclass:: sdbus_async.networkmanager.WpaSecurityFlags - :members: +There are two types of enums. ``IntEnum`` is used for a discrete values +and ``IntFlag`` is used for bit flags. For example, :py:class:`DeviceType ` +identifies a single device type as a device cannot be of multiple types. +:py:class:`WifiCapabilitiesFlags ` +shows a particular Wifi device capabilities which it can have multiple, for example, supporting +both 5GHz and 2.4GHz radio bands. -.. autoclass:: sdbus_async.networkmanager.WiFiOperationMode - :members: +Usually ``IntEnum`` is implied unless the enum's name ends with ``Flag``. -.. autoclass:: sdbus_async.networkmanager.SecretAgentCapabilities - :members: +Example code using enums: -.. autoclass:: sdbus_async.networkmanager.ConnectionState - :members: +.. code-block:: python -.. autoclass:: sdbus_async.networkmanager.ConnectionStateFlags - :members: + from sdbus_async.networkmanager.enums import DeviceType, WifiCapabilitiesFlags -.. autoclass:: sdbus_async.networkmanager.ConnectionStateReason - :members: + # Get particular device type from an integer + DeviceType(2) == DeviceType.WIFI + # Returns: True -.. autoclass:: sdbus_async.networkmanager.BluetoothCapabilities - :members: + # Check if a specific flag is enabled + WifiCapabilitiesFlags.FREQ_2GHZ in WifiCapabilitiesFlags(0x00000400 | 0x00000200) + # Returns: True -.. autoclass:: sdbus_async.networkmanager.IpTunnelMode - :members: + # Iterate over all enabled flags + list(WifiCapabilitiesFlags(0x00000400 | 0x00000200)) + # Returns: [, ] -.. autoclass:: sdbus_async.networkmanager.ModemCapabilities - :members: +`See Python's standard library documentation for more detailed +tutorial and API reference. `_ -.. autoclass:: sdbus_async.networkmanager.WirelessCapabilities - :members: +NetworkManager's enums +------------------------- -.. autoclass:: sdbus_async.networkmanager.DeviceCapabilities - :members: - -.. autoclass:: sdbus_async.networkmanager.DeviceState - :members: - -.. autoclass:: sdbus_async.networkmanager.DeviceStateReason - :members: - -.. autoclass:: sdbus_async.networkmanager.DeviceType - :members: - -.. autoclass:: sdbus_async.networkmanager.DeviceMetered - :members: - -.. autoclass:: sdbus_async.networkmanager.ConnectivityState - :members: - -.. autoclass:: sdbus_async.networkmanager.DeviceInterfaceFlags - :members: - -.. autoclass:: sdbus_async.networkmanager.ConnectionFlags - :members: - -.. autoclass:: sdbus_async.networkmanager.VpnState - :members: - -.. autoclass:: sdbus_async.networkmanager.VpnFailure - :members: - -.. autoclass:: sdbus_async.networkmanager.NetworkManagerConnectivityState - :members: - -.. autoclass:: sdbus_async.networkmanager.NetworkManagerState - :members: +.. automodule:: sdbus_async.networkmanager.enums + :members: Helper classes ----------------------- diff --git a/docs/index.rst b/docs/index.rst index e7c9bfd..3003952 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ of `NetworkManager `_. :maxdepth: 3 :caption: Contents: + quickstart objects examples device_interfaces @@ -21,5 +22,5 @@ of `NetworkManager `_. enums exceptions -See `python-sdbus `_ homepage if you are +See `python-sdbus `_ homepage if you are unfamiliar with python-sdbus. diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 0000000..17d3880 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,134 @@ +NetworkManager D-Bus API quickstart +=================================== + +This is a tutorial to understand the structure of the NetworkManager +D-Bus API and how to use it. + +.. note:: + + NetworkManager usually uses the system D-Bus, however, sdbus uses the session + D-Bus by default. It is recommend to call ``sdbus.set_default_bus(sdbus.sd_bus_open_system())`` + to set the system bus as default bus. + +NetworkManager main object +-------------------------- + +This is a static object that contains information about +entire state of NetworkManager. The Python class +:py:class:`NetworkManager ` +has a predefined service name and object path. + +.. code-block:: python + + import sdbus + from sdbus_block.networkmanager import NetworkManager + + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + network_manager = NetworkManager() + +Devices +------- + +Every device object represents a network device. Device object has a generic +methods and properties that are universal across all device types and +a type specific methods. The :py:class:`NetworkDeviceGeneric +` implements generic methods +and, for example, :py:class:`NetworkDeviceWireless ` +adds Wi-Fi specific methods. + +The :py:attr:`device_type ` +property and enum :py:class:`DeviceType ` +can be used to determine particular type of a device. + +.. code-block:: python + + import sdbus + + from sdbus_block.networkmanager import ( + NetworkDeviceGeneric, + NetworkDeviceWireless, + NetworkManager, + ) + from sdbus_block.networkmanager.enums import DeviceType + + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + network_manager = NetworkManager() + + all_devices = {path: NetworkDeviceGeneric(path) for path in network_manager.devices} + + wifi_devices = [ + NetworkDeviceWireless(path) + for path, device in all_devices.items() + if device.device_type == DeviceType.WIFI + ] + +Connection +---------- + +Connection represents a configuration containing an IP address, Wifi password, +proxy settings and etc... The main object to access connections is the +:py:class:`NetworkManagerSettings ` +which has predefined object path. + +Each individual connection has a separate path which can be accessed with +:py:class:`NetworkConnectionSettings ` +class. + +.. code-block:: python + + import sdbus + + from sdbus_block.networkmanager import ( + NetworkConnectionSettings, + NetworkManager, + NetworkManagerSettings, + ) + + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + network_manager = NetworkManager() + + networwork_manager_settings = NetworkManagerSettings() + + all_connections = [ + NetworkConnectionSettings(x) for x in networwork_manager_settings.connections + ] + +The actual connection settings are represented by a complex double nested dictionary +of D-Bus variants. For convenience a `dataclass `_ +based helper is provided. + +The :py:meth:`get_profile ` +and :py:meth:`update_profile ` +are two main methods to interact with connection settings helper. + +.. code-block:: python + + import sdbus + + from sdbus_block.networkmanager import ( + NetworkConnectionSettings, + NetworkManager, + NetworkManagerSettings, + ) + + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + network_manager = NetworkManager() + + networwork_manager_settings = NetworkManagerSettings() + + + connection = NetworkConnectionSettings(networwork_manager_settings.connections[0]) + setting_dataclass = connection.get_profile() + print("uuid:", setting_dataclass.connection.uuid) + +Active Connection +----------------- + +:py:class:`ActiveConnection ` +is a product of a Connection being applied to a Device. + +For example, :py:meth:`activate_connection ` +of the main NetworkManager object will create new Active Connection +(therefore configuring network on a device) and return its path. +The :py:meth:`deactivate_connection ` +will remove the active connection and remove the device's network configuration. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..7c490fe --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# Copyright (C) 2023 igo95862 +sphinx_rtd_theme +sdbus>=0.10.2 diff --git a/examples/async/add-eth-connection-async.py b/examples/2.0.0/async/add-eth-connection-async.py similarity index 100% rename from examples/async/add-eth-connection-async.py rename to examples/2.0.0/async/add-eth-connection-async.py diff --git a/examples/2.0.0/async/add-openvpn-connection-async.py b/examples/2.0.0/async/add-openvpn-connection-async.py new file mode 100755 index 0000000..4200ebd --- /dev/null +++ b/examples/2.0.0/async/add-openvpn-connection-async.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to create a new VPN network connection profile. Currently supported only with tls-auth +# +# $ examples/async/add-openvpn-connection-async.py --help +# usage: add-openvpn-connection.py [-h] [-c CONN_ID] [-d DEV] [--remote REMOTE] [--remote-cert-tls] [-a] [--save] +# [--ca, CA_PATH] [--cert, CERT_PATH] [--key, KEY_path] [--ta, TA_PATH] +# +# Optional arguments have example values: +# +# optional arguments: +# -h, --help show this help message and exit +# -c CONN_ID Connection Id +# -u UUID Connection UUID +# -f OVPN .ovpn connection file +# -a autoconnect +# --save Save +# --ca Path to CA file +# --cert Path to cert file +# --key Path to key file +# --ta Path to tls-auth file +# +# $ add-vpn-connection.py +# New unsaved connection profile created, show it with: +# nmcli connection show "MyConnectionExample"|grep -v -e -- -e default +# +# Connection Profile settings are described at: +# https://networkmanager.dev/docs/api/latest/ref-settings.html +# +# Note: By default, it uses add_connection_unsaved() to add a temporary +# memory-only connection which is not saved to the system-connections folder: +# For reference, see: https://networkmanager.dev/docs/api/latest/spec.html +# -> org.freedesktop.NetworkManager.Settings (Settings Profile Manager) + +import asyncio +import functools +import logging +import sdbus +from uuid import uuid4 +from argparse import ArgumentParser +from pprint import pformat +from sdbus_async.networkmanager import ( + NetworkManagerSettings as SettingsManager, + ConnectionType, +) +from sdbus_async.networkmanager.settings import ( + ConnectionProfile, + ConnectionSettings, + Ipv4Settings, + Ipv6Settings, + VpnSettings +) + + +async def add_vpn_connection_async(conn_id: str, + dev: str, + remote: str, + remote_cert_tls: str, + uuid, + auto: bool, + save: bool, + ca: str, + cert: str, + key: str, + ta: str) -> str: + # Add a temporary (not yet saved) network connection profile + # param Namespace args: dev, remote, remote_cert_tls, ca_path, cert_path, key_path, ta_path + # return: dbus connection path of the created connection profile + + info = logging.getLogger().info + + # If we add many connections passing the same id, things get messy. Check: + if await SettingsManager().get_connections_by_id(conn_id): + print(f'Connection "{conn_id}" exists, remove it first') + print(f'Run: nmcli connection delete "{conn_id}"') + return "" + + profile = ConnectionProfile( + connection=ConnectionSettings( + connection_id=conn_id, + uuid=str(uuid), + connection_type=ConnectionType.VPN.value, + autoconnect=bool(auto), + ), + ipv4=Ipv4Settings(method="auto"), + ipv6=Ipv6Settings(method="auto"), + vpn=VpnSettings(data={ + 'ca': ca, + 'cert': cert, + 'cert-pass-flags': '0', + 'connection-type': 'tls', + 'dev': dev, + 'key': key, + 'remote': remote, + 'remote-cert-tls': remote_cert_tls, + 'ta': ta, + 'ta-dir': '1' + }, service_type='org.freedesktop.NetworkManager.openvpn') + ) + + s = SettingsManager() + save = bool(save) + addconnection = s.add_connection if save else s.add_connection_unsaved + connection_settings_dbus_path = await addconnection(profile.to_dbus()) + created = "created and saved" if save else "created" + info(f"New unsaved connection profile {created}, show it with:") + info(f'nmcli connection show "{conn_id}"|grep -v -e -- -e default') + info("Settings used:") + info(functools.partial(pformat, sort_dicts=False)(profile.to_settings_dict())) + return connection_settings_dbus_path + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.INFO) + p = ArgumentParser(description="Optional arguments have example values:") + conn_id = "MyConnectionExample" + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") + p.add_argument("--dev", dest="dev", default="tun", help="VPN Dev") + p.add_argument("--remote", dest="remote", default="example.com:443:tcp", help="VPN Remote") + p.add_argument("--remote-cert-tls", dest="remote_cert_tls", default="server", help="VPN Remote cert tls") + p.add_argument("--ca", dest="ca", required=True, help="VPN CA file path") + p.add_argument("--cert", dest="cert", required=True, help="VPN cert file path") + p.add_argument("--key", dest="key", required=True, help="VPN key file path") + p.add_argument("--ta", dest="ta", required=True, help="VPN TA file path") + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") + p.add_argument("--save", dest="save", action="store_true", help="Save") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + if connection_dpath := asyncio.run(add_vpn_connection_async(**vars(args))): + print(f"Path of the new connection: {connection_dpath}") + print(f"UUID of the new connection: {args.uuid}") + else: + print("Error: No new connection created.") diff --git a/examples/async/add-wifi-psk-connection-async.py b/examples/2.0.0/async/add-wifi-psk-connection-async.py similarity index 100% rename from examples/async/add-wifi-psk-connection-async.py rename to examples/2.0.0/async/add-wifi-psk-connection-async.py diff --git a/examples/async/delete-connection-by-uuid-async.py b/examples/2.0.0/async/delete-connection-by-uuid-async.py similarity index 100% rename from examples/async/delete-connection-by-uuid-async.py rename to examples/2.0.0/async/delete-connection-by-uuid-async.py diff --git a/examples/async/device-state-async.py b/examples/2.0.0/async/device-state-async.py similarity index 100% rename from examples/async/device-state-async.py rename to examples/2.0.0/async/device-state-async.py diff --git a/examples/async/list-connections-async.py b/examples/2.0.0/async/list-connections-async.py similarity index 100% rename from examples/async/list-connections-async.py rename to examples/2.0.0/async/list-connections-async.py diff --git a/examples/2.0.0/async/listen-device-changes-async.py b/examples/2.0.0/async/listen-device-changes-async.py new file mode 100644 index 0000000..725010f --- /dev/null +++ b/examples/2.0.0/async/listen-device-changes-async.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later +# Copyright (C) 2025 igo95862 +# +# Example of listening to device state change signals. +# Also shows the use of enums. +from __future__ import annotations + +import asyncio +from argparse import ArgumentParser + +import sdbus + +from sdbus_async.networkmanager import NetworkDeviceGeneric, NetworkManager +from sdbus_async.networkmanager.enums import DeviceState, DeviceStateReason + + +async def listen_device(device_path: str, device_name: str) -> None: + generic_device = NetworkDeviceGeneric(device_path) + print(f"Listening state changes for device {device_name!r}") + + async for ( + new_state, + old_state, + reason, + ) in generic_device.state_changed.catch(): + print( + f"Now {DeviceState(new_state).name}, " + f"was {DeviceState(old_state).name}, " + f"reason {DeviceStateReason(reason).name}" + ) + + +async def main() -> None: + arg_parser = ArgumentParser() + arg_parser.add_argument("device_name") + + args = arg_parser.parse_args() + + network_manager = NetworkManager() + + device_name = args.device_name + device_path = await network_manager.get_device_by_ip_iface(device_name) + + # If you use create_task() make sure to keep a reference to the + # task or it will get garbage collected. + await listen_device(device_path, device_name) + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + asyncio.run(main()) diff --git a/examples/async/netdevinfo-async.py b/examples/2.0.0/async/netdevinfo-async.py similarity index 100% rename from examples/async/netdevinfo-async.py rename to examples/2.0.0/async/netdevinfo-async.py diff --git a/examples/async/update-connection-async.py b/examples/2.0.0/async/update-connection-async.py similarity index 100% rename from examples/async/update-connection-async.py rename to examples/2.0.0/async/update-connection-async.py diff --git a/examples/block/add-eth-connection.py b/examples/2.0.0/block/add-eth-connection.py similarity index 100% rename from examples/block/add-eth-connection.py rename to examples/2.0.0/block/add-eth-connection.py diff --git a/examples/block/add-wifi-psk-connection.py b/examples/2.0.0/block/add-wifi-psk-connection.py similarity index 100% rename from examples/block/add-wifi-psk-connection.py rename to examples/2.0.0/block/add-wifi-psk-connection.py diff --git a/examples/block/delete-connection-by-uuid.py b/examples/2.0.0/block/delete-connection-by-uuid.py similarity index 100% rename from examples/block/delete-connection-by-uuid.py rename to examples/2.0.0/block/delete-connection-by-uuid.py diff --git a/examples/block/device-state.py b/examples/2.0.0/block/device-state.py similarity index 100% rename from examples/block/device-state.py rename to examples/2.0.0/block/device-state.py diff --git a/examples/block/list-connections.py b/examples/2.0.0/block/list-connections.py similarity index 100% rename from examples/block/list-connections.py rename to examples/2.0.0/block/list-connections.py diff --git a/examples/block/netdevinfo.py b/examples/2.0.0/block/netdevinfo.py similarity index 100% rename from examples/block/netdevinfo.py rename to examples/2.0.0/block/netdevinfo.py diff --git a/examples/block/update-connection.py b/examples/2.0.0/block/update-connection.py similarity index 100% rename from examples/block/update-connection.py rename to examples/2.0.0/block/update-connection.py diff --git a/examples/dev/async/add-eth-connection-async.py b/examples/dev/async/add-eth-connection-async.py new file mode 100755 index 0000000..f7be36f --- /dev/null +++ b/examples/dev/async/add-eth-connection-async.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to create a new Ethernet network connection profile: +# +# examples/block/add-eth-connection-async.py --help +# usage: add-eth-connection-async.py [-h] [-c CONN_ID] [-u UUID] [-4 IP4] +# [-i INTERFACE_NAME] [-g GW] [-a] [--save] +# +# Optional arguments have example values: +# +# optional arguments: +# -h, --help show this help message and exit +# -c CONN_ID Connection Id +# -u UUID Connection UUID +# -i INTERFACE_NAME ethX device +# -4 IP4 IP4/prefix +# -g GW gw/metric +# -a autoconnect +# --save Save +# +# Connection Profile settings are described at: +# https://networkmanager.dev/docs/api/latest/ref-settings.html + +import asyncio +import sdbus +import functools +import logging +import pprint +import sys +from uuid import uuid4 +from argparse import ArgumentParser, Namespace +from sdbus_async.networkmanager import NetworkManagerSettings +from sdbus_async.networkmanager import NetworkManagerConnectionProperties + + +async def add_ethernet_connection_async(args: Namespace) -> str: + """Add a (by default) temporary (not yet saved) network connection profile + :param Namespace args: autoconnect, conn_id, psk, save, ssid, uuid + :return: dbus connection path of the created connection profile + """ + info = logging.getLogger().info + + # If we add many connections using the same id, things get messy. Check: + if await NetworkManagerSettings().get_connections_by_id(args.conn_id): + info(f'Connections using ID "{args.conn_id}" exist, remove them:') + info(f'Run: nmcli connection delete "{args.conn_id}"') + return "" + + ipaddr, prefix = args.ip4.split("/") + properties: NetworkManagerConnectionProperties = { + "connection": { + "id": ("s", args.conn_id), + "uuid": ("s", str(args.uuid)), + "type": ("s", "802-3-ethernet"), + "autoconnect": ("b", args.auto), + }, + "ipv4": { + "method": ("s", "manual"), + "address-data": ( + "aa{sv}", + [ + { + "address": ("s", ipaddr), + "prefix": ("u", int(prefix)), + }, + ], + ), + }, + "ipv6": {"method": ("s", "disabled")}, + } + if args.interface_name: + properties["connection"]["interface-name"] = ("s", args.interface_name) + if len(sys.argv) == 1 or args.gw != "192.0.2.1/4000": + default_gateway, route_metric = args.gw.split("/") + properties["ipv4"]["gateway"] = ("s", default_gateway) + properties["ipv4"]["route-metric"] = ("u", int(route_metric)) + + s = NetworkManagerSettings() + addconnection = s.add_connection if args.save else s.add_connection_unsaved + connection_settings_dbus_path = await addconnection(properties) + created = "created and saved" if args.save else "created" + + info(f"New unsaved connection profile {created}, show it with:") + info(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') + info("Settings used:") + info(functools.partial(pprint.pformat, sort_dicts=False)(properties)) + return connection_settings_dbus_path + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.INFO) + p = ArgumentParser(description="Optional arguments have example values:") + conn_id = "MyConnectionExample" + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") + p.add_argument("-i", dest="interface_name", default="", help="ethX device") + p.add_argument("-4", dest="ip4", default="192.0.2.8/24", help="IP4/prefix") + p.add_argument("-g", dest="gw", default="192.0.2.1/4000", help="gw/metric") + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") + p.add_argument("--save", dest="save", action="store_true", help="Save") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + if connection_dpath := asyncio.run(add_ethernet_connection_async(args)): + print(f"Path of the new connection: {connection_dpath}") + print(f"UUID of the new connection: {args.uuid}") + else: + print("Error: No new connection created.") diff --git a/examples/dev/async/add-openvpn-connection-async.py b/examples/dev/async/add-openvpn-connection-async.py new file mode 100755 index 0000000..4200ebd --- /dev/null +++ b/examples/dev/async/add-openvpn-connection-async.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to create a new VPN network connection profile. Currently supported only with tls-auth +# +# $ examples/async/add-openvpn-connection-async.py --help +# usage: add-openvpn-connection.py [-h] [-c CONN_ID] [-d DEV] [--remote REMOTE] [--remote-cert-tls] [-a] [--save] +# [--ca, CA_PATH] [--cert, CERT_PATH] [--key, KEY_path] [--ta, TA_PATH] +# +# Optional arguments have example values: +# +# optional arguments: +# -h, --help show this help message and exit +# -c CONN_ID Connection Id +# -u UUID Connection UUID +# -f OVPN .ovpn connection file +# -a autoconnect +# --save Save +# --ca Path to CA file +# --cert Path to cert file +# --key Path to key file +# --ta Path to tls-auth file +# +# $ add-vpn-connection.py +# New unsaved connection profile created, show it with: +# nmcli connection show "MyConnectionExample"|grep -v -e -- -e default +# +# Connection Profile settings are described at: +# https://networkmanager.dev/docs/api/latest/ref-settings.html +# +# Note: By default, it uses add_connection_unsaved() to add a temporary +# memory-only connection which is not saved to the system-connections folder: +# For reference, see: https://networkmanager.dev/docs/api/latest/spec.html +# -> org.freedesktop.NetworkManager.Settings (Settings Profile Manager) + +import asyncio +import functools +import logging +import sdbus +from uuid import uuid4 +from argparse import ArgumentParser +from pprint import pformat +from sdbus_async.networkmanager import ( + NetworkManagerSettings as SettingsManager, + ConnectionType, +) +from sdbus_async.networkmanager.settings import ( + ConnectionProfile, + ConnectionSettings, + Ipv4Settings, + Ipv6Settings, + VpnSettings +) + + +async def add_vpn_connection_async(conn_id: str, + dev: str, + remote: str, + remote_cert_tls: str, + uuid, + auto: bool, + save: bool, + ca: str, + cert: str, + key: str, + ta: str) -> str: + # Add a temporary (not yet saved) network connection profile + # param Namespace args: dev, remote, remote_cert_tls, ca_path, cert_path, key_path, ta_path + # return: dbus connection path of the created connection profile + + info = logging.getLogger().info + + # If we add many connections passing the same id, things get messy. Check: + if await SettingsManager().get_connections_by_id(conn_id): + print(f'Connection "{conn_id}" exists, remove it first') + print(f'Run: nmcli connection delete "{conn_id}"') + return "" + + profile = ConnectionProfile( + connection=ConnectionSettings( + connection_id=conn_id, + uuid=str(uuid), + connection_type=ConnectionType.VPN.value, + autoconnect=bool(auto), + ), + ipv4=Ipv4Settings(method="auto"), + ipv6=Ipv6Settings(method="auto"), + vpn=VpnSettings(data={ + 'ca': ca, + 'cert': cert, + 'cert-pass-flags': '0', + 'connection-type': 'tls', + 'dev': dev, + 'key': key, + 'remote': remote, + 'remote-cert-tls': remote_cert_tls, + 'ta': ta, + 'ta-dir': '1' + }, service_type='org.freedesktop.NetworkManager.openvpn') + ) + + s = SettingsManager() + save = bool(save) + addconnection = s.add_connection if save else s.add_connection_unsaved + connection_settings_dbus_path = await addconnection(profile.to_dbus()) + created = "created and saved" if save else "created" + info(f"New unsaved connection profile {created}, show it with:") + info(f'nmcli connection show "{conn_id}"|grep -v -e -- -e default') + info("Settings used:") + info(functools.partial(pformat, sort_dicts=False)(profile.to_settings_dict())) + return connection_settings_dbus_path + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.INFO) + p = ArgumentParser(description="Optional arguments have example values:") + conn_id = "MyConnectionExample" + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") + p.add_argument("--dev", dest="dev", default="tun", help="VPN Dev") + p.add_argument("--remote", dest="remote", default="example.com:443:tcp", help="VPN Remote") + p.add_argument("--remote-cert-tls", dest="remote_cert_tls", default="server", help="VPN Remote cert tls") + p.add_argument("--ca", dest="ca", required=True, help="VPN CA file path") + p.add_argument("--cert", dest="cert", required=True, help="VPN cert file path") + p.add_argument("--key", dest="key", required=True, help="VPN key file path") + p.add_argument("--ta", dest="ta", required=True, help="VPN TA file path") + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") + p.add_argument("--save", dest="save", action="store_true", help="Save") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + if connection_dpath := asyncio.run(add_vpn_connection_async(**vars(args))): + print(f"Path of the new connection: {connection_dpath}") + print(f"UUID of the new connection: {args.uuid}") + else: + print("Error: No new connection created.") diff --git a/examples/dev/async/add-wifi-psk-connection-async.py b/examples/dev/async/add-wifi-psk-connection-async.py new file mode 100755 index 0000000..fb9022a --- /dev/null +++ b/examples/dev/async/add-wifi-psk-connection-async.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to create a new WiFi-PSK network connection profile: +# +# $ examples/async/add-wifi-psk-connection-async.py --help +# usage: add-wifi-psk-connection.py [-h] [-c CONN_ID] [-s SSID] [-p PSK] +# [-i INTERFACE_NAME] [-a] [--save] +# +# Optional arguments have example values: +# +# optional arguments: +# -h, --help show this help message and exit +# -c CONN_ID Connection Id +# -u UUID Connection UUID +# -s SSID WiFi SSID +# -p PSK WiFi PSK +# -i INTERFACE_NAME WiFi device +# -a autoconnect +# --save Save +# +# $ add-wifi-psk-connection.py +# New unsaved connection profile created, show it with: +# nmcli connection show "MyConnectionExample"|grep -v -e -- -e default +# Settings used: +# {'connection': {'id': ('s', 'MyConnectionExample'), +# 'uuid': ('s', '90e3bcc8-d3a5-4725-a363-abb788fd47bf'), +# 'type': ('s', '802-11-wireless'), +# 'autoconnect': ('b', False)}, +# '802-11-wireless': {'mode': ('s', 'infrastructure'), +# 'security': ('s', '802-11-wireless-security'), +# 'ssid': ('ay', b'CafeSSID')}, +# '802-11-wireless-security': {'key-mgmt': ('s', 'wpa-psk'), +# 'auth-alg': ('s', 'open'), +# 'psk': ('s', 'Coffee!!')}, +# 'ipv4': {'method': ('s', 'auto')}, +# 'ipv6': {'method': ('s', 'auto')}} +# +# Connection Profile settings are described at: +# https://networkmanager.dev/docs/api/latest/ref-settings.html +# +# Note: By default, it uses add_connection_unsaved() to add a temporary +# memory-only connection which is not saved to the system-connections folder: +# For reference, see: https://networkmanager.dev/docs/api/latest/spec.html +# -> org.freedesktop.NetworkManager.Settings (Settings Profile Manager) + +import asyncio +import binascii +import functools +import logging +import sdbus +from uuid import uuid4 +from argparse import ArgumentParser, Namespace +from passlib.utils.pbkdf2 import pbkdf2 # type: ignore +from pprint import pformat +from sdbus_async.networkmanager import ( + NetworkManagerSettings as SettingsManager, + ConnectionType, +) +from sdbus_async.networkmanager.settings import ( + ConnectionProfile, + ConnectionSettings, + Ipv4Settings, + Ipv6Settings, + WirelessSettings, + WirelessSecuritySettings, +) + + +async def add_wifi_psk_connection_async(args: Namespace) -> str: + """Add a temporary (not yet saved) network connection profile + :param Namespace args: autoconnect, conn_id, psk, save, ssid, uuid + :return: dbus connection path of the created connection profile + """ + info = logging.getLogger().info + + # If we add many connections passing the same id, things get messy. Check: + if await SettingsManager().get_connections_by_id(args.conn_id): + print(f'Connection "{args.conn_id}" exists, remove it first') + print(f'Run: nmcli connection delete "{args.conn_id}"') + return "" + + if args.key_mgmt == "wpa-psk" and len(args.password) < 64: + # Hash the password into a psk hash to not store it in clear form: + pw = pbkdf2(args.password.encode(), args.ssid.encode(), 4096, 32) + args.password = binascii.hexlify(pw).decode("utf-8") + + profile = ConnectionProfile( + connection=ConnectionSettings( + connection_id=args.conn_id, + uuid=str(args.uuid), + connection_type=ConnectionType.WIFI.value, + autoconnect=bool(hasattr(args, "auto") and args.auto), + ), + ipv4=Ipv4Settings(method="auto"), + ipv6=Ipv6Settings(method="auto"), + wireless=WirelessSettings(ssid=args.ssid.encode("utf-8")), + wireless_security=WirelessSecuritySettings( + key_mgmt=args.key_mgmt, auth_alg="open", psk=args.password + ), + ) + + # To bind the new connection to a specific interface, use this: + if hasattr(args, "interface_name") and args.interface_name: + profile.connection.interface_name = args.interface_name + + s = SettingsManager() + save = bool(hasattr(args, "save") and args.save) + addconnection = s.add_connection if save else s.add_connection_unsaved + connection_settings_dbus_path = await addconnection(profile.to_dbus()) + created = "created and saved" if save else "created" + info(f"New unsaved connection profile {created}, show it with:") + info(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') + info("Settings used:") + info(functools.partial(pformat, sort_dicts=False)(profile.to_settings_dict())) + return connection_settings_dbus_path + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.INFO) + p = ArgumentParser(description="Optional arguments have example values:") + conn_id = "MyConnectionExample" + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") + p.add_argument("-s", dest="ssid", default="CafeSSID", help="WiFi SSID") + p.add_argument("-k", dest="key_mgmt", default="wpa-psk", help="key-mgmt") + p.add_argument("-p", dest="password", default="Coffee!!", help="WiFi PSK") + p.add_argument("-i", dest="interface_name", default="", help="WiFi device") + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") + p.add_argument("--save", dest="save", action="store_true", help="Save") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + if connection_dpath := asyncio.run(add_wifi_psk_connection_async(args)): + print(f"Path of the new connection: {connection_dpath}") + print(f"UUID of the new connection: {args.uuid}") + else: + print("Error: No new connection created.") diff --git a/examples/dev/async/delete-connection-by-uuid-async.py b/examples/dev/async/delete-connection-by-uuid-async.py new file mode 100755 index 0000000..ac1ee3d --- /dev/null +++ b/examples/dev/async/delete-connection-by-uuid-async.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Create and delete a connection profile using the unique connection uuid +# +import asyncio +import logging +import sdbus +from uuid import uuid4 +from argparse import Namespace +from sdbus_async.networkmanager import NetworkManagerSettings +from sdbus_async.networkmanager import NmSettingsInvalidConnectionError + + +async def delete_connection_by_uuid(uuid: str) -> bool: + """Find and delete the connection identified by the given UUID""" + try: + await NetworkManagerSettings().delete_connection_by_uuid(uuid) + except NmSettingsInvalidConnectionError: + logging.getLogger().fatal(f"Connection {uuid} for deletion not found") + return False + return True + + +async def create_and_delete_wifi_psk_connection_async(args: Namespace) -> bool: + """Add a temporary (not yet saved) network connection profile + :param Namespace args: autoconnect, conn_id, psk, save, ssid, uuid + :return: dbus connection path of the created connection profile + """ + add_wifi_psk_connection = __import__("add-wifi-psk-connection-async") + if not await add_wifi_psk_connection.add_wifi_psk_connection_async(args): + return False + return await delete_connection_by_uuid(str(args.uuid)) + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.WARNING) + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + args = Namespace(conn_id="Example", uuid=uuid4(), ssid="S", psk="Password") + if asyncio.run(create_and_delete_wifi_psk_connection_async(args)): + print(f"Succeeded in creating and deleting connection {args.uuid}") diff --git a/examples/dev/async/device-state-async.py b/examples/dev/async/device-state-async.py new file mode 100755 index 0000000..558ed3a --- /dev/null +++ b/examples/dev/async/device-state-async.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to list the network devices including type, state, internet +# connectivitycheck state and the identifier of the active connection. +# +# NetworkDeviceGeneric/org.freedesktop.NetworkManager.Device is described at +# https://networkmanager.dev/docs/api/latest/ref-dbus-devices.html +# +# The output resembles the output of the NM CLI command "nmcli device": +# +# Interface Type State Internet Connection +# lo Generic Unmanaged Unknown +# wlp0s20f3 Wifi Activated Full Wolke7 [primary connection] +# docker0 Bridge Activated None docker0 +# enx0c3796090408 Ethernet Activated Full enx0c3796090408 +# p2p-dev-wlp0s20f3 Wifi_P2P Disconnected None + +import argparse +import asyncio +import sdbus +from sdbus_async.networkmanager import ( + NetworkManager, + NetworkDeviceGeneric, + DeviceState, + DeviceType, + DeviceCapabilitiesFlags, + ActiveConnection, + ConnectivityState, +) +from enum import Enum + + +def title(enum: Enum) -> str: + """Get the name of an enum: 1st character is uppercase, rest lowercase""" + return enum.name.title() + + +async def list_active_hardware_networkdevice_states(only_hw: bool) -> None: + """Print the list of activated network devices similar to nmcli device""" + nm = NetworkManager() + devices_paths = await nm.get_devices() + + print("Interface Type State Internet Connection") + for device_path in devices_paths: + generic = NetworkDeviceGeneric(device_path) + + # Demonstrates an enum to match devices using capabilities: + if only_hw and ( + DeviceCapabilitiesFlags.IS_SOFTWARE + in DeviceCapabilitiesFlags(await generic.capabilities)): + continue + + # Create the strings for the columns using the names of the enums: + dev: str = await generic.interface + dtype = title(DeviceType(await generic.device_type)) + state = title(DeviceState(await generic.state)) + connectivity = title(ConnectivityState(await generic.ip4_connectivity)) + + name: str = "" + if await generic.active_connection != "/": # Connection is active + # ActiveConnection() gets propertites from active connection path: + active_conn = ActiveConnection(await generic.active_connection) + name = await active_conn.id + if await active_conn.default: + name += " [primary connection]" + + print(f"{dev:<17} {dtype:<8} {state:<12} {connectivity:<8} {name:<14}") + + +if __name__ == "__main__": + p = argparse.ArgumentParser() + p.add_argument("--hw", action="store_true", dest="only_hw", help="Only HW") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + asyncio.run(list_active_hardware_networkdevice_states(args.only_hw)) diff --git a/examples/dev/async/list-connections-async.py b/examples/dev/async/list-connections-async.py new file mode 100755 index 0000000..cd39131 --- /dev/null +++ b/examples/dev/async/list-connections-async.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example which lists the details of NetworkManager's connection profiles. +# This is the asyncio variant of this example using sdbus_async.networkmanager. +# The blocking variant of this example is examples/block/list-connections.py +# +# Configuration settings are described at +# https://networkmanager.dev/docs/api/latest/ref-settings.html +# +# Example output: +# | name: Wired connection 1 +# | uuid: b2caabdc-98bb-3f88-8d28-d10369d6ded9 +# | type: 802-3-ethernet +# | interface-name: enx001e101f0000 +# | ipv4: method: manual +# | ipaddr: 192.168.178.34/24 +# | route-metric: 200 +# | ipv6: method: disabled +import asyncio +import sdbus +import pprint +from sdbus_async.networkmanager import ( + NetworkManagerSettings, + NetworkConnectionSettings, +) +from typing import List + + +async def list_connection_profiles_async() -> None: + networkmanager_settings = NetworkManagerSettings() + connections_paths: List[str] = await networkmanager_settings.connections + for dbus_connection_path in connections_paths: + await print_connection_profile(dbus_connection_path) + + +async def print_connection_profile(connection_path: str) -> None: + connectionsettings_service = NetworkConnectionSettings(connection_path) + profile = await connectionsettings_service.get_profile() + connection = profile.connection + print("-------------------------------------------------------------") + print("name:", connection.connection_id) + print("uuid:", connection.uuid) + print("type:", connection.connection_type) + if connection.interface_name: + print(" interface-name:", connection.interface_name) + if profile.ipv4: + print("ipv4: method:", profile.ipv4.method) + if profile.ipv4.address_data: + for address in profile.ipv4.address_data: + print(f' ipaddr: {address.address}/{address.prefix}') + if profile.ipv4.route_metric: + print(f' route-metric: {profile.ipv4.route_metric}') + if profile.ipv6: + print("ipv6: method:", profile.ipv6.method) + pprint.pprint(profile.to_settings_dict(), sort_dicts=False) + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + asyncio.run(list_connection_profiles_async()) diff --git a/examples/dev/async/listen-device-changes-async.py b/examples/dev/async/listen-device-changes-async.py new file mode 100644 index 0000000..725010f --- /dev/null +++ b/examples/dev/async/listen-device-changes-async.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later +# Copyright (C) 2025 igo95862 +# +# Example of listening to device state change signals. +# Also shows the use of enums. +from __future__ import annotations + +import asyncio +from argparse import ArgumentParser + +import sdbus + +from sdbus_async.networkmanager import NetworkDeviceGeneric, NetworkManager +from sdbus_async.networkmanager.enums import DeviceState, DeviceStateReason + + +async def listen_device(device_path: str, device_name: str) -> None: + generic_device = NetworkDeviceGeneric(device_path) + print(f"Listening state changes for device {device_name!r}") + + async for ( + new_state, + old_state, + reason, + ) in generic_device.state_changed.catch(): + print( + f"Now {DeviceState(new_state).name}, " + f"was {DeviceState(old_state).name}, " + f"reason {DeviceStateReason(reason).name}" + ) + + +async def main() -> None: + arg_parser = ArgumentParser() + arg_parser.add_argument("device_name") + + args = arg_parser.parse_args() + + network_manager = NetworkManager() + + device_name = args.device_name + device_path = await network_manager.get_device_by_ip_iface(device_name) + + # If you use create_task() make sure to keep a reference to the + # task or it will get garbage collected. + await listen_device(device_path, device_name) + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + asyncio.run(main()) diff --git a/examples/dev/async/netdevinfo-async.py b/examples/dev/async/netdevinfo-async.py new file mode 100755 index 0000000..e4ef140 --- /dev/null +++ b/examples/dev/async/netdevinfo-async.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to list the active IPv4 protocol configuration of network devices +# and the current status of WiFi adapters +# +# For IPv4 and org.freedesktop.NetworkManager.Device.Wireless see: +# https://networkmanager.dev/docs/api/latest/settings-ipv4.html +# https://networkmanager.dev/docs/api/latest/ref-dbus-devices.html +import asyncio +import sdbus +from sdbus_async.networkmanager import ( + ConnectionType, + NetworkConnectionSettings, + NetworkManager, + NetworkManagerSettings, + NetworkDeviceGeneric, + IPv4Config, + DeviceType, + NetworkDeviceWireless, + WiFiOperationMode, + AccessPoint, +) +from typing import Any, Dict, List, Optional, Tuple +NetworkManagerAddressData = List[Dict[str, Tuple[str, Any]]] + + +async def get_most_recent_connection_id(ifname: str, dev_type: str) -> Optional[str]: + """Return the most-recently used connection_id for this device + + Besides getting the currently active connection, this will succeed + in getting the most recent connection when a device is not connected + at the moment this function is executed. + + It uses getattr(ConnectionType, dev_type) to get the connection_type + used for connection_profiles for this DeviceType. + + With a slight modification, this could return the most recent connections + of the given device, ordered by the time of the last use of them. + """ + settings_service = NetworkManagerSettings() + connection_paths: List[str] = await settings_service.connections + conns = {} + for connection_path in connection_paths: + connection_manager = NetworkConnectionSettings(connection_path) + connection = (await connection_manager.get_profile()).connection + # Filter connection profiles matching the connection type for the device: + if connection.connection_type != getattr(ConnectionType, dev_type): + continue + # If the interface_name of a connection profiles is set, it must match: + if connection.interface_name and connection.interface_name != ifname: + continue + # If connection.timestamp is not set, it was never active. Set it to 0: + if not connection.timestamp: + connection.timestamp = 0 + # Record the connection_ids of the matches, and timestamp is the key: + conns[connection.timestamp] = connection.connection_id + if not len(conns): + return None + # Returns the connection_id of the highest timestamp which was found: + return conns.get(max(conns.keys())) + + +async def list_networkdevice_details_async() -> None: + nm = NetworkManager() + devices_paths = await nm.get_devices() + + for device_path in devices_paths: + generic_device = NetworkDeviceGeneric(device_path) + device_ip4_conf_path: str = await generic_device.ip4_config + if device_ip4_conf_path == "/": + continue + if not await generic_device.managed: + continue + dev_type = DeviceType(await generic_device.device_type).name + if dev_type == DeviceType.BRIDGE.name: + continue + + dev_name = await generic_device.interface + ip4_conf = IPv4Config(device_ip4_conf_path) + gateway: str = await ip4_conf.gateway + + print("Type: ", dev_type.title()) + print("Name: ", dev_name) + + if gateway: + print("Gateway:", gateway) + + address_data: NetworkManagerAddressData = await ip4_conf.address_data + for inetaddr in address_data: + print(f'Address: {inetaddr["address"][1]}/{inetaddr["prefix"][1]}') + + nameservers: NetworkManagerAddressData = await ip4_conf.nameserver_data + for dns in nameservers: + print("DNS: ", dns["address"][1]) + + if dev_type == DeviceType.WIFI.name: + wifi = NetworkDeviceWireless(device_path) + print("Wifi: ", WiFiOperationMode(await wifi.mode).name.title()) + ap_path = await wifi.active_access_point + if ap_path == "/": + print("No active access point") + else: + ap = AccessPoint(ap_path) + ssid: bytes = await ap.ssid + if ssid: + print("SSID: ", ssid.decode("utf-8", "ignore")) + if await ap.strength: + print("Signal: ", await ap.strength) + connection_id = await get_most_recent_connection_id(dev_name, dev_type) + if connection_id: + print("Profile:", connection_id) + + print("") + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + asyncio.run(list_networkdevice_details_async()) diff --git a/examples/dev/async/update-connection-async.py b/examples/dev/async/update-connection-async.py new file mode 100755 index 0000000..d838bda --- /dev/null +++ b/examples/dev/async/update-connection-async.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Update a property of a connection profile, looked up by connection id +# +# This version uses connection_manager.get_profile().to_settings_dict() +# to retrieve the connection profile from NetworkManager as a settings dict. +# +# It then updates it dynamically using the given arguments: +# The default is to set ipv4.dns-search to ["domain1.com", "domain2.com"]. +# +# The dynamically updated dict is then used to update connection profile of NM. +# +# The IPv4 settings of connections profiles are documented here: +# https://networkmanager.dev/docs/api/latest/settings-ipv4.html +# +import asyncio +import sdbus +from functools import partial +from sdbus_async.networkmanager import NetworkManagerSettings +from sdbus_async.networkmanager import NetworkConnectionSettings +from sdbus_async.networkmanager.settings import ConnectionProfile +from pprint import pprint +from typing import Any, Dict + + +async def update_connection_async(args: Dict[str, Any]) -> None: + """Update the settings for [key][entry] of the 1st matching connection""" + + # Get the connection path of the connection(s) with the received id + fn = NetworkManagerSettings().get_connections_by_id(args["connection_id"]) + connection_paths = await fn + if not connection_paths: + print(f"No connection {id}, create with add-wifi-psk-connection-async") + return + + # Get the profile settings of the first connection with given id + connection_manager = NetworkConnectionSettings(connection_paths[0]) + existing_connection_profile = await connection_manager.get_profile() + settings = existing_connection_profile.to_settings_dict() + + # Update the given setting's property using the given value + setting, property = args["connection_setting"] + settings[setting][property] = args["value"] + + # Get a new ConnectionProfile with the change incorporated + new_connection_profile = ConnectionProfile.from_settings_dict(settings) + + # Update the new ConnectionProfile in NetworkManager's configuration + await connection_manager.update(new_connection_profile.to_dbus()) + + print(f'Updated {new_connection_profile.connection.uuid}.{setting}:') + partial(pprint, sort_dicts=False)(settings) + + # Restore the previous connection profile: + await connection_manager.update(existing_connection_profile.to_dbus()) + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + args = { + # Set MyConnectionExample.ipv4.dns-search to "domain1.com,domain2.com": + "connection_id": "MyConnectionExample", + "connection_setting": ("ipv4", "dns-search"), + "value": ["domain1.com", "domain2.com"], + } + asyncio.run(update_connection_async(args)) diff --git a/examples/dev/block/add-eth-connection.py b/examples/dev/block/add-eth-connection.py new file mode 100755 index 0000000..b692890 --- /dev/null +++ b/examples/dev/block/add-eth-connection.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to create a new Ethernet network connection profile: +# +# examples/block/add-eth-connection.py --help +# usage: add-eth-connection.py [-h] [-c CONN_ID] [-u UUID] [-i INTERFACE_NAME] +# [-4 IP4] [-g GW] [-a] [--save] +# +# Optional arguments have example values: +# +# optional arguments: +# -h, --help show this help message and exit +# -c CONN_ID Connection Id +# -u UUID Connection UUID +# -i INTERFACE_NAME ethX device +# -4 IP4 IP4/prefix +# -g GW gw/metric +# -a autoconnect +# --save Save +# +# Connection Profile settings are described at: +# https://networkmanager.dev/docs/api/latest/ref-settings.html + +import sdbus +import functools +import logging +import pprint +import sys +from uuid import uuid4 +from argparse import ArgumentParser, Namespace +from sdbus_block.networkmanager import NetworkManagerSettings +from sdbus_block.networkmanager import NetworkManagerConnectionProperties + + +def add_ethernet_connection(args: Namespace) -> str: + """Add a (by default) temporary (not yet saved) network connection profile + :param Namespace args: autoconnect, conn_id, psk, save, ssid, uuid + :return: dbus connection path of the created connection profile + """ + info = logging.getLogger().info + + # If we add many connections using the same id, things get messy. Check: + if NetworkManagerSettings().get_connections_by_id(args.conn_id): + info(f'Connections using ID "{args.conn_id}" exist, remove them:') + info(f'Run: nmcli connection delete "{args.conn_id}"') + return "" + + ipaddr, prefix = args.ip4.split("/") + properties: NetworkManagerConnectionProperties = { + "connection": { + "id": ("s", args.conn_id), + "uuid": ("s", str(args.uuid)), + "type": ("s", "802-3-ethernet"), + "autoconnect": ("b", args.auto), + }, + "ipv4": { + "method": ("s", "manual"), + "address-data": ( + "aa{sv}", + [ + { + "address": ("s", ipaddr), + "prefix": ("u", int(prefix)), + }, + ], + ), + }, + "ipv6": {"method": ("s", "disabled")}, + } + if args.interface_name: + properties["connection"]["interface-name"] = ("s", args.interface_name) + if len(sys.argv) == 1 or args.gw != "192.0.2.1/4000": + default_gateway, route_metric = args.gw.split("/") + properties["ipv4"]["gateway"] = ("s", default_gateway) + properties["ipv4"]["route-metric"] = ("u", int(route_metric)) + + s = NetworkManagerSettings() + addconnection = s.add_connection if args.save else s.add_connection_unsaved + connection_settings_dbus_path = addconnection(properties) + created = "created and saved" if args.save else "created" + + info(f"New unsaved connection profile {created}, show it with:") + info(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') + info("Settings used:") + info(functools.partial(pprint.pformat, sort_dicts=False)(properties)) + return connection_settings_dbus_path + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.INFO) + p = ArgumentParser(description="Optional arguments have example values:") + conn_id = "MyConnectionExample" + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") + p.add_argument("-i", dest="interface_name", default="", help="ethX device") + p.add_argument("-4", dest="ip4", default="192.0.2.8/24", help="IP4/prefix") + p.add_argument("-g", dest="gw", default="192.0.2.1/4000", help="gw/metric") + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") + p.add_argument("--save", dest="save", action="store_true", help="Save") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + if connection_dpath := add_ethernet_connection(args): + print(f"Path of the new connection: {connection_dpath}") + print(f"UUID of the new connection: {args.uuid}") + else: + print("Error: No new connection created.") diff --git a/examples/dev/block/add-wifi-psk-connection.py b/examples/dev/block/add-wifi-psk-connection.py new file mode 100755 index 0000000..3b732be --- /dev/null +++ b/examples/dev/block/add-wifi-psk-connection.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to create a new WiFi-PSK network connection profile: +# +# $ examples/block/add-wifi-psk-connection.py --help +# usage: add-wifi-psk-connection.py [-h] [-c CONN_ID] [-s SSID] [-p PSK] +# [-i INTERFACE_NAME] [-a] [--save] +# +# Optional arguments have example values: +# +# optional arguments: +# -h, --help show this help message and exit +# -c CONN_ID Connection Id +# -u UUID Connection UUID +# -s SSID WiFi SSID +# -p PSK WiFi PSK +# -i INTERFACE_NAME WiFi device +# -a autoconnect +# --save Save +# +# $ add-wifi-psk-connection.py +# New unsaved connection profile created, show it with: +# nmcli connection show "MyConnectionExample"|grep -v -e -- -e default +# Settings used: +# {'connection': {'id': ('s', 'MyConnectionExample'), +# 'uuid': ('s', '90e3bcc8-d3a5-4725-a363-abb788fd47bf'), +# 'type': ('s', '802-11-wireless'), +# 'autoconnect': ('b', False)}, +# '802-11-wireless': {'mode': ('s', 'infrastructure'), +# 'security': ('s', '802-11-wireless-security'), +# 'ssid': ('ay', b'CafeSSID')}, +# '802-11-wireless-security': {'key-mgmt': ('s', 'wpa-psk'), +# 'auth-alg': ('s', 'open'), +# 'psk': ('s', 'Coffee!!')}, +# 'ipv4': {'method': ('s', 'auto')}, +# 'ipv6': {'method': ('s', 'auto')}} +# +# Connection Profile settings are described at: +# https://networkmanager.dev/docs/api/latest/ref-settings.html +# +# Note: By default, it uses add_connection_unsaved() to add a temporary +# memory-only connection which is not saved to the system-connections folder: +# For reference, see: https://networkmanager.dev/docs/api/latest/spec.html +# -> org.freedesktop.NetworkManager.Settings (Settings Profile Manager) + +import functools +import logging +import pprint +import sdbus +from uuid import uuid4 +from argparse import ArgumentParser, Namespace +from sdbus_block.networkmanager import NetworkManagerSettings +from sdbus_block.networkmanager import NetworkManagerConnectionProperties + + +def add_wifi_psk_connection(args: Namespace) -> str: + """Add a temporary (not yet saved) network connection profile + :param Namespace args: autoconnect, conn_id, psk, save, ssid, uuid + :return: dbus connection path of the created connection profile + """ + info = logging.getLogger().info + + # If we add many connections passing the same id, things get messy. Check: + if NetworkManagerSettings().get_connections_by_id(args.conn_id): + print(f'Connection "{args.conn_id}" exists, remove it first') + print(f'Run: nmcli connection delete "{args.conn_id}"') + return "" + + properties: NetworkManagerConnectionProperties = { + "connection": { + "id": ("s", args.conn_id), + "uuid": ("s", str(args.uuid)), + "type": ("s", "802-11-wireless"), + "autoconnect": ("b", bool(hasattr(args, "auto") and args.auto)), + }, + "802-11-wireless": { + "mode": ("s", "infrastructure"), + "security": ("s", "802-11-wireless-security"), + "ssid": ("ay", args.ssid.encode("utf-8")), + }, + "802-11-wireless-security": { + "key-mgmt": ("s", "wpa-psk"), + "auth-alg": ("s", "open"), + "psk": ("s", args.psk), + }, + "ipv4": {"method": ("s", "auto")}, + "ipv6": {"method": ("s", "auto")}, + } + + # To bind the new connection to a specific interface, use this: + if hasattr(args, "interface_name") and args.interface_name: + properties["connection"]["interface-name"] = ("s", args.interface_name) + + s = NetworkManagerSettings() + save = bool(hasattr(args, "save") and args.save) + addconnection = s.add_connection if save else s.add_connection_unsaved + connection_settings_dbus_path = addconnection(properties) + created = "created and saved" if save else "created" + info(f"New unsaved connection profile {created}, show it with:") + info(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') + info("Settings used:") + info(functools.partial(pprint.pformat, sort_dicts=False)(properties)) + return connection_settings_dbus_path + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.INFO) + p = ArgumentParser(description="Optional arguments have example values:") + conn_id = "MyConnectionExample" + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") + p.add_argument("-s", dest="ssid", default="CafeSSID", help="WiFi SSID") + p.add_argument("-p", dest="psk", default="Coffee!!", help="WiFi PSK") + p.add_argument("-i", dest="interface_name", default="", help="WiFi device") + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") + p.add_argument("--save", dest="save", action="store_true", help="Save") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + if connection_dpath := add_wifi_psk_connection(args): + print(f"Path of the new connection: {connection_dpath}") + print(f"UUID of the new connection: {args.uuid}") + else: + print("Error: No new connection created.") diff --git a/examples/dev/block/delete-connection-by-uuid.py b/examples/dev/block/delete-connection-by-uuid.py new file mode 100755 index 0000000..2b42509 --- /dev/null +++ b/examples/dev/block/delete-connection-by-uuid.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Create and delete a connection profile using the unique connection uuid +# +import logging +import sdbus +from uuid import uuid4 +from argparse import Namespace +from sdbus_block.networkmanager import NetworkManagerSettings +from sdbus_block.networkmanager import NmSettingsInvalidConnectionError + + +def delete_connection_by_uuid(uuid: str) -> bool: + """Find and delete the connection identified by the given UUID""" + try: + NetworkManagerSettings().delete_connection_by_uuid(uuid) + except NmSettingsInvalidConnectionError: + logging.getLogger().fatal(f"Connection {uuid} for deletion not found") + return False + return True + + +def create_and_delete_wifi_psk_connection_async(args: Namespace) -> bool: + """Add a temporary (not yet saved) network connection profile + :param Namespace args: autoconnect, conn_id, psk, save, ssid, uuid + :return: dbus connection path of the created connection profile + """ + add_wifi_psk_connection = __import__("add-wifi-psk-connection") + if not add_wifi_psk_connection.add_wifi_psk_connection(args): + return False + return delete_connection_by_uuid(str(args.uuid)) + + +if __name__ == "__main__": + logging.basicConfig(format="%(message)s", level=logging.WARNING) + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + args = Namespace(conn_id="Example", uuid=uuid4(), ssid="S", psk="Password") + if create_and_delete_wifi_psk_connection_async(args): + print(f"Succeeded in creating and deleting connection {args.uuid}") diff --git a/examples/dev/block/device-state.py b/examples/dev/block/device-state.py new file mode 100755 index 0000000..138e163 --- /dev/null +++ b/examples/dev/block/device-state.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to list the network devices including type, state, internet +# connectivitycheck state and the identifier of the active connection. +# +# NetworkDeviceGeneric/org.freedesktop.NetworkManager.Device is described at +# https://networkmanager.dev/docs/api/latest/ref-dbus-devices.html +# +# The output resembles the output of the NM CLI command "nmcli device": +# +# Interface Type State Internet Connection +# lo Generic Unmanaged Unknown +# wlp0s20f3 Wifi Activated Full Wolke7 [primary connection] +# docker0 Bridge Activated None docker0 +# enx0c3796090408 Ethernet Activated Full enx0c3796090408 +# p2p-dev-wlp0s20f3 Wifi_P2P Disconnected None + +import argparse +import sdbus +from sdbus_block.networkmanager import ( + NetworkManager, + NetworkDeviceGeneric, + DeviceState, + DeviceType, + DeviceCapabilitiesFlags, + ActiveConnection, + ConnectivityState, +) +from enum import Enum + + +def title(enum: Enum) -> str: + """Get the name of an enum: 1st character is uppercase, rest lowercase""" + return enum.name.title() + + +def list_active_hardware_networkdevice_states(only_hw: bool) -> None: + """Print the list of activated network devices similar to nmcli device""" + nm = NetworkManager() + devices_paths = nm.get_devices() + + print("Interface Type State Internet Connection") + for device_path in devices_paths: + generic_dev = NetworkDeviceGeneric(device_path) + + # Demonstrates an enum to match devices using capabilities: + if only_hw and ( + DeviceCapabilitiesFlags.IS_SOFTWARE + in DeviceCapabilitiesFlags(generic_dev.capabilities)): + continue + + # Create the strings for the columns using the names of the enums: + dev = generic_dev.interface + type = title(DeviceType(generic_dev.device_type)) + state = title(DeviceState(generic_dev.state)) + connectivity = title(ConnectivityState(generic_dev.ip4_connectivity)) + + if generic_dev.active_connection == "/": # No active connection + id = "" + else: + # ActiveConnection() gets propertites from active connection path: + active_connection = ActiveConnection(generic_dev.active_connection) + id = active_connection.id + if active_connection.default: + id += " [primary connection]" + + print(f"{dev:<17} {type:<8} {state:<12} {connectivity:<8} {id:<14}") + + +if __name__ == "__main__": + p = argparse.ArgumentParser() + p.add_argument("--hw", action="store_true", dest="only_hw", help="Only HW") + args = p.parse_args() + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + list_active_hardware_networkdevice_states(args.only_hw) diff --git a/examples/dev/block/list-connections.py b/examples/dev/block/list-connections.py new file mode 100755 index 0000000..f8d262a --- /dev/null +++ b/examples/dev/block/list-connections.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example which lists the details of NetworkManager's connection profiles. +# +# Configuration settings are described at +# https://networkmanager.dev/docs/api/latest/ref-settings.html +# +# Example output: +# | name: Wired connection 1 +# | uuid: b2caabdc-98bb-3f88-8d28-d10369d6ded9 +# | type: 802-3-ethernet +# | interface-name: enx001e101f0000 +# | ipv4: method: manual +# | ipaddr: 192.168.178.34/24 +# | route-metric: 200 +# | ipv6: method: disabled +import sdbus +from sdbus_block.networkmanager import ( + ConnectionType, + NetworkManagerSettings, + NetworkConnectionSettings, +) + + +def list_connection_profiles_blocking() -> None: + """Call print_connection_profile_blocking() for all connection profiles""" + networkmanager_settings = NetworkManagerSettings() + for dbus_connection_path in networkmanager_settings.connections: + print_connection_profile_blocking(dbus_connection_path) + + +def print_connection_profile_blocking(connection_path: str) -> None: + """Show the use of NetworkConnectionSettings(path).get_profile()""" + profile = NetworkConnectionSettings(connection_path).get_profile() + print("-------------------------------------------------------------") + print("name:", profile.connection.connection_id) + print("uuid:", profile.connection.uuid) + print("type:", profile.connection.connection_type) + if profile.connection.interface_name: + print(" interface-name:", profile.connection.interface_name) + if profile.ipv4: + print("ipv4: method:", profile.ipv4.method) + if profile.ipv4.address_data: + for address in profile.ipv4.address_data: + print(f' ipaddr: {address.address}/{address.prefix}') + if profile.ipv4.route_metric: + print(f' route-metric: {profile.ipv4.route_metric}') + if profile.ipv6: + print("ipv6: method:", profile.ipv6.method) + if profile.connection.connection_type == ConnectionType.WIFI: + assert profile.wireless + assert profile.wireless.ssid + print("ssid:", profile.wireless.ssid.decode()) + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + list_connection_profiles_blocking() diff --git a/examples/dev/block/netdevinfo.py b/examples/dev/block/netdevinfo.py new file mode 100755 index 0000000..1dec341 --- /dev/null +++ b/examples/dev/block/netdevinfo.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Example to list the active IPv4 protocol configuration of network devices +# and the current status of WiFi adapters +# +# For IPv4 and org.freedesktop.NetworkManager.Device.Wireless see: +# https://networkmanager.dev/docs/api/latest/settings-ipv4.html +# https://networkmanager.dev/docs/api/latest/ref-dbus-devices.html +import sdbus +from sdbus_block.networkmanager import ( + ConnectionType, + NetworkConnectionSettings, + NetworkManager, + NetworkManagerSettings, + NetworkDeviceGeneric, + IPv4Config, + DeviceType, + NetworkDeviceWireless, + WiFiOperationMode, + AccessPoint, +) +from typing import Any, Dict, List, Optional, Tuple +NetworkManagerAddressData = List[Dict[str, Tuple[str, Any]]] + + +def get_most_recent_connection_id(ifname: str, dev_type: str) -> Optional[str]: + """Return the most-recently used connection_id for this device + + Besides getting the currently active connection, this will succeed + in getting the most recent connection when a device is not connected + at the moment this function is executed. + + It uses getattr(ConnectionType, dev_type) to get the connection_type + used for connection_profiles for this DeviceType. + + With a slight modification, this could return the most recent connections + of the given device, ordered by the time of the last use of them. + """ + settings_service = NetworkManagerSettings() + connection_paths: List[str] = settings_service.connections + conns = {} + for connection_path in connection_paths: + connection_manager = NetworkConnectionSettings(connection_path) + connection = connection_manager.get_profile().connection + # Filter connection profiles matching the connection type for the device: + if connection.connection_type != getattr(ConnectionType, dev_type): + continue + # If the interface_name of a connection profiles is set, it must match: + if connection.interface_name and connection.interface_name != ifname: + continue + # If connection.timestamp is not set, it was never active. Set it to 0: + if not connection.timestamp: + connection.timestamp = 0 + # Record the connection_ids of the matches, and timestamp is the key: + conns[connection.timestamp] = connection.connection_id + if not len(conns): + return None + # Returns the connection_id of the highest timestamp which was found: + return conns.get(max(conns.keys())) + + +def list_networkdevice_details_blocking() -> None: + + for device_path in NetworkManager().get_devices(): + generic_device = NetworkDeviceGeneric(device_path) + device_ip4_conf_path: str = generic_device.ip4_config + if device_ip4_conf_path == "/": + continue + if not generic_device.managed: + continue + dev_type = DeviceType(generic_device.device_type).name + if dev_type == DeviceType.BRIDGE.name: + continue + + dev_name = generic_device.interface + ip4_conf = IPv4Config(device_ip4_conf_path) + gateway: str = ip4_conf.gateway + + print("Type: ", dev_type.title()) + print("Name: ", dev_name) + + if gateway: + print("Gateway:", gateway) + + address_data: NetworkManagerAddressData = ip4_conf.address_data + for inetaddr in address_data: + print(f'Address: {inetaddr["address"][1]}/{inetaddr["prefix"][1]}') + + nameservers: NetworkManagerAddressData = ip4_conf.nameserver_data + for dns in nameservers: + print("DNS: ", dns["address"][1]) + + if dev_type == DeviceType.WIFI.name: + wifi = NetworkDeviceWireless(device_path) + print("Wifi: ", WiFiOperationMode(wifi.mode).name.title()) + ap = AccessPoint(wifi.active_access_point) + ssid: bytes = ap.ssid + if ssid: + print("SSID: ", ssid.decode("utf-8", "ignore")) + if ap.strength: + print("Signal: ", ap.strength) + connection_id = get_most_recent_connection_id(dev_name, dev_type) + if connection_id: + print("Profile:", connection_id) + + print("") + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + list_networkdevice_details_blocking() diff --git a/examples/dev/block/update-connection.py b/examples/dev/block/update-connection.py new file mode 100644 index 0000000..10d86f1 --- /dev/null +++ b/examples/dev/block/update-connection.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Update a property of a connection profile, looked up by connection id +# +# The IPv4 settings of connections profiles are documented here: +# https://networkmanager.dev/docs/api/latest/settings-ipv4.html +# + +import sdbus +from functools import partial +from sdbus_block.networkmanager import NetworkManagerSettings +from sdbus_block.networkmanager import NetworkConnectionSettings +from pprint import pprint +from typing import Any, Dict + + +def update_connection(args: Dict[str, Any]) -> None: + """Update the settings for [key][entry] of the 1st matching connection""" + con = NetworkManagerSettings().get_connections_by_id(args["connection_id"]) + settings_domain, setting = args["connection_setting"] + if con: + connection_settings = NetworkConnectionSettings(con[0]) + properties = connection_settings.get_settings() + # For compatibility with old tools, NM adds and prefers them, delete: + properties["ipv4"].pop("addresses") # -> Use ["ipv4"]["address-data"] + properties["ipv4"].pop("routes") # ----> Use ["ipv4"]["route-data"] + + # Update the setting's value in the given configuration group: + properties[settings_domain][setting] = args["value"] + connection_settings.update(properties) + + print(f'Updated {properties["connection"]["uuid"]}.{settings_domain}:') + partial(pprint, sort_dicts=False)(properties[settings_domain]) + else: + print(f"No connection matching {id}") + + +if __name__ == "__main__": + sdbus.set_default_bus(sdbus.sd_bus_open_system()) + args = { + # Set MyConnectionExample.ipv4.dns-search to "domain1.com,domain2.com": + "connection_id": "MyConnectionExample", + "connection_setting": ("ipv4", "dns-search"), + # "as" is the so-called DBus signature, it means "array of strings": + "value": ("as", ["domain1.com", "domain2.com"]), + } + update_connection(args) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index c64eda8..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sdbus>=0.10.2 diff --git a/sdbus_async/networkmanager/__init__.py b/sdbus_async/networkmanager/__init__.py index 6f81a22..637346f 100644 --- a/sdbus_async/networkmanager/__init__.py +++ b/sdbus_async/networkmanager/__init__.py @@ -20,30 +20,46 @@ from __future__ import annotations from .enums import ( - AccessPointCapabilities, - BluetoothCapabilities, - ConnectionFlags, - ConnectionState, - ConnectionStateFlags, - ConnectionStateReason, + ActivationStateFlags, + ActiveConnectionState, + ActiveConnectionStateReason, + BluetoothCapabilitiesFlags, + CheckpointCreateFlags, + CheckpointRollbackResult, + ClientPermission, + ClientPermissionResult, + ConnectionMultiConnect, ConnectionType, ConnectivityState, - DeviceCapabilities, + DeviceCapabilitiesFlags, DeviceInterfaceFlags, DeviceMetered, + DeviceReapplyFlags, DeviceState, DeviceStateReason, DeviceType, IpTunnelMode, - ModemCapabilities, - NetworkManagerConnectivityState, + ModemCapabilitiesFlags, + MptcpFlags, + NetworkManagerCapabilitiesFlags, + NetworkManagerReloadFlags, NetworkManagerState, - SecretAgentCapabilities, + NetworkManagerVersionInfoCapability, + RadioFlags, + SecretAgentCapabilitiesFlags, + SecretAgentGetSecretsFlags, + SettingsAddConnection2Flags, + SettingsConnectionFlags, + SettingsUpdate2Flags, + VpnConnectionState, + VpnConnectionStateReason, VpnFailure, - VpnState, + VpnServiceState, + WifiAccessPointCapabilitiesFlags, + WifiAccessPointSecurityFlags, + WifiCapabilitiesFlags, WiFiOperationMode, - WirelessCapabilities, - WpaSecurityFlags, + WimaxNSPNetworkType, ) from .exceptions import ( NetworkManagerAlreadyAsleepOrAwakeError, @@ -218,30 +234,46 @@ __all__ = ( # .enums - 'AccessPointCapabilities', - 'BluetoothCapabilities', - 'ConnectionFlags', - 'ConnectionState', - 'ConnectionStateFlags', - 'ConnectionStateReason', + 'ActivationStateFlags', + 'ActiveConnectionState', + 'ActiveConnectionStateReason', + 'BluetoothCapabilitiesFlags', + 'CheckpointCreateFlags', + 'CheckpointRollbackResult', + 'ClientPermission', + 'ClientPermissionResult', + 'ConnectionMultiConnect', 'ConnectionType', 'ConnectivityState', - 'DeviceCapabilities', + 'DeviceCapabilitiesFlags', 'DeviceInterfaceFlags', 'DeviceMetered', + 'DeviceReapplyFlags', 'DeviceState', 'DeviceStateReason', 'DeviceType', 'IpTunnelMode', - 'ModemCapabilities', - 'NetworkManagerConnectivityState', + 'ModemCapabilitiesFlags', + 'MptcpFlags', + 'NetworkManagerCapabilitiesFlags', + 'NetworkManagerReloadFlags', 'NetworkManagerState', - 'SecretAgentCapabilities', + 'NetworkManagerVersionInfoCapability', + 'RadioFlags', + 'SecretAgentCapabilitiesFlags', + 'SecretAgentGetSecretsFlags', + 'SettingsAddConnection2Flags', + 'SettingsConnectionFlags', + 'SettingsUpdate2Flags', + 'VpnConnectionState', + 'VpnConnectionStateReason', 'VpnFailure', - 'VpnState', + 'VpnServiceState', + 'WifiAccessPointCapabilitiesFlags', + 'WifiAccessPointSecurityFlags', + 'WifiCapabilitiesFlags', 'WiFiOperationMode', - 'WirelessCapabilities', - 'WpaSecurityFlags', + 'WimaxNSPNetworkType', # .exceptions 'NetworkManagerAlreadyAsleepOrAwakeError', 'NetworkManagerAlreadyEnabledOrDisabledError', diff --git a/sdbus_async/networkmanager/enums.py b/sdbus_async/networkmanager/enums.py index e1b09bc..77ed1c4 100644 --- a/sdbus_async/networkmanager/enums.py +++ b/sdbus_async/networkmanager/enums.py @@ -17,755 +17,1504 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +"""Enums used by the NetworkManager. + +`Copied from NetworkManager documentation. +`_ +""" from __future__ import annotations from enum import Enum, IntEnum, IntFlag -class AccessPointCapabilities(IntFlag): - """Wi-Fi Access point capabilities +class NetworkManagerVersionInfoCapability(IntEnum): + """The numeric values represent the bit index of the capability. - Flags: + These capabilities can be queried in the ``version_info`` D-Bus property. - * NONE - * PRIVACY - * WPS - * WPS_BUTTON - * WPS_PIN + Since NetworkManager 1.42. """ - NONE = 0x0 - PRIVACY = 0x1 - WPS = 0x2 - WPS_BUTTON = 0x4 - WPS_PIN = 0x8 - - -class WpaSecurityFlags(IntFlag): - """WPA (WiFi protected Access) encryption and authentication types - - Flags: - - * NONE - * P2P_WEP40 - * P2P_WEP104 - * P2P_TKIP - * P2P_CCMP - * BROADCAST_WEP40 - * BROADCAST_WEP104 - * BROADCAST_TKIP - * BROADCAST_CCMP - * AUTH_PSK - * AUTH_802_1X - * AUTH_SAE - * AUTH_OWE - * AUTH_OWE_TM - * AUTH_EAP_SUITE_B + + SYNC_ROUTE_WITH_TABLE = 0 + """Contains the fix to a bug that caused that routes in table other + than main were not removed on reapply nor on connection down. """ - NONE = 0x0 - P2P_WEP40 = 0x1 - P2P_WEP104 = 0x2 - P2P_TKIP = 0x4 - P2P_CCMP = 0x8 - BROADCAST_WEP40 = 0x10 - BROADCAST_WEP104 = 0x20 - BROADCAST_TKIP = 0x40 - BROADCAST_CCMP = 0x80 - AUTH_PSK = 0x100 - AUTH_802_1X = 0x200 - AUTH_SAE = 0x400 - AUTH_OWE = 0x800 - AUTH_OWE_TM = 0x1000 - AUTH_EAP_SUITE_B = 0x2000 + IP4_FORWARDING = 1 + """Indicates that NetworkManager supports configuring per-device IPv4 + sysctl forwarding setting. + Since NetworkManager 1.54. + """ + SRIOV_PRESERVE_ON_DOWN = 2 + """NetworkManager supports the "sriov.preserve-on-down" property. + + Since NetworkManager 1.54. + """ -class WiFiOperationMode(IntEnum): - """Operation mode of WiFi access point - * UNKNOWN - * ADHOC - * INFRASTRUCTURE - * AP - * MESH +class NetworkManagerCapabilitiesFlags(IntFlag): + """NetworkManager loaded plugins. + + Capabilities are positive numbers. They are part of stable API and + a certain capability number is guaranteed not to change. + + The range 0x7000 - 0x7FFF of capabilities is guaranteed not to be + used by upstream NetworkManager. It could thus be used for + downstream extensions. + + Since NetworkManager 1.6. + """ + + TEAM = 0x1 + """Teams can be managed. This means the team device plugin is loaded.""" + OVS = 0x2 + """OpenVSwitch can be managed. This means the OVS device plugin is loaded. + + Since NetworkManager 1.24. """ + + +class NetworkManagerState(IntEnum): + """Indicates the current overall networking state.""" + UNKNOWN = 0 - ADHOC = 1 - INFRASTRUCTURE = 2 - AP = 3 - MESH = 4 + """Networking state is unknown. + This indicates a daemon error that makes it unable to reasonably assess + the state. In such event the applications are expected to assume Internet + connectivity might be present and not disable controls that require + network access. The graphical shells may hide the network accessibility + indicator altogether since no meaningful status indication can be provided. + """ + ASLEEP = 10 + """Networking is not enabled, the system is being suspended or resumed + from suspend.""" + DISCONNECTED = 20 + """There is no active network connection. -class SecretAgentCapabilities(IntFlag): - """Secret agent capabilities + The graphical shell should indicate no network connectivity and + the applications should not attempt to access the network. + """ + DISCONNECTING = 30 + """Network connections are being cleaned up. - Flags: + The applications should tear down their network sessions. + """ + CONNECTING = 40 + """A network connection is being started. - * NONE - * VPN_HINTS + The graphical shell should indicate the network is being connected + while the applications should still make no attempts to connect + the network. """ - NONE = 0x0 - VPN_HINTS = 0x1 + CONNECTED_LOCAL = 50 + """There is only local IPv4 and/or IPv6 connectivity, but no default route + to access the Internet. + The graphical shell should indicate no network connectivity. + """ + CONNECTED_SITE = 60 + """There is only site-wide IPv4 and/or IPv6 connectivity. -class ConnectionState(IntEnum): - """State of the connection + This means a default route is available, but the Internet + connectivity check (see "Connectivity" property) did not succeed. + The graphical shell should indicate limited network connectivity. + """ + GLOBAL = 70 + """There is global IPv4 and/or IPv6 Internet connectivity. - * UNKNOWN - * ACTIVATING - * ACTIVATED - * DEACTIVATING - * DEACTIVATED + This means the Internet connectivity check succeeded, the graphical shell + should indicate full network connectivity. """ + + +class ConnectivityState(IntEnum): + """System connectivity state.""" + UNKNOWN = 0 - ACTIVATING = 1 - ACTIVATED = 2 - DEACTIVATING = 3 - DEACTIVATED = 4 + """Network connectivity is unknown. + + This means the connectivity checks are disabled (e.g. on server + installations) or has not run yet. The graphical shell should assume + the Internet connection might be available and not present a captive + portal window.""" + NONE = 1 + """The host is not connected to any network. + + There's no active connection that contains a default route to the internet + and thus it makes no sense to even attempt a connectivity check. + The graphical shell should use this state to indicate the network + connection is unavailable.""" + PORTAL = 2 + """The Internet connection is hijacked by a captive portal gateway. + + The graphical shell may open a sandboxed web browser window (because + the captive portals typically attempt a man-in-the-middle attacks against + the https connections) for the purpose of authenticating to a gateway and + retrigger the connectivity check with CheckConnectivity() when the browser + window is dismissed.""" + LIMITED = 3 + """The host is connected to a network, does not appear to be able to reach + the full Internet, but a captive portal has not been detected.""" + FULL = 4 + """The host is connected to a network, and appears to be able to reach the + full Internet.""" -class ConnectionStateFlags(IntFlag): - """State of connection flags +class DeviceType(IntEnum): + """Indicate the type of hardware represented by a device object.""" + + UNKNOWN = 0 + """Unknown device.""" + ETHERNET = 1 + """A wired ethernet device.""" + WIFI = 2 + """An 802.11 Wi-Fi device.""" + UNUSED1 = 3 + """Not used.""" + UNUSED2 = 4 + """Not used.""" + BLUETOOTH = 5 + """A Bluetooth device supporting PAN or DUN access protocols.""" + OLPC_MESH = 6 + """An OLPC XO mesh networking device.""" + WIMAX = 7 + """An 802.16e Mobile WiMAX broadband device.""" + MODEM = 8 + """A modem supporting analog telephone, CDMA/EVDO, GSM/UMTS, or + LTE network access protocols.""" + INFINIBAND = 9 + """An IP-over-InfiniBand device.""" + BOND = 10 + """A bond master interface.""" + VLAN = 11 + """An 802.1Q VLAN interface.""" + ADSL = 12 + """ADSL modem.""" + BRIDGE = 13 + """A bridge master interface.""" + GENERIC = 14 + """Generic support for unrecognized device types.""" + TEAM = 15 + """A team master interface.""" + TUN = 16 + """A TUN or TAP interface.""" + IP_TUNNEL = 17 + """A IP tunnel interface.""" + MACVLAN = 18 + """A MACVLAN interface.""" + VXLAN = 19 + """A VXLAN interface.""" + VETH = 20 + """A VETH interface.""" + MACSEC = 21 + """A MACsec interface.""" + DUMMY = 22 + """A dummy interface.""" + PPP = 23 + """A PPP interface.""" + OVS_INTERFACE = 24 + """A Open vSwitch interface.""" + OVS_PORT = 25 + """A Open vSwitch port.""" + OVS_BRIDGE = 26 + """A Open vSwitch bridge.""" + WPAN = 27 + """A IEEE 802.15.4 (WPAN) MAC Layer Device.""" + SIXLOWPAN = 28 + """6LoWPAN interface.""" + WIREGUARD = 29 + """A WireGuard interface.""" + WIFI_P2P = 30 + """An 802.11 Wi-Fi P2P device. - Flags: + Since NetworkManager 1.16. + """ + VRF = 31 + """A VRF (Virtual Routing and Forwarding) interface. - * NONE - * IS_MASTER - * IS_SLAVE - * LAYER2_READY - * IP4_READY - * IP6_READY - * MASTER_HAS_SLAVES - * LIFE_TIME_BOUND_TO_PROFILE_VISIBILITY - * EXTERNAL + Since NetworkManager 1.24. """ - NONE = 0x0 - IS_MASTER = 0x1 - IS_SLAVE = 0x2 - LAYER2_READY = 0x4 - IP4_READY = 0x8 - IP6_READY = 0x10 - MASTER_HAS_SLAVES = 0x20 - LIFE_TIME_BOUND_TO_PROFILE_VISIBILITY = 0x40 - EXTERNAL = 0x80 + LOOPBACK = 32 + """A loopback interface. + Since NetworkManager 1.42. + """ + HSR = 33 + """A HSR/PRP device. -class ConnectionStateReason(IntEnum): - """Connection state change reason - - * UNKNOWN - * NONE - * USER_DISCONNECTED - * DEVICE_DISCONNECTED - * SERVICE_STOPPED - * IP_CONFIG_INVALID - * CONNECT_TIMEOUT - * SERVICE_START_TIMEOUT - * SERVICE_START_FAILED - * NO_SECRETS - * LOGIN_FAILED - * CONNECTION_REMOVED - * DEPENDENCY_FAILED - * DEVICE_REALIZE_FAILED - * DEVICE_REMOVED + Since NetworkManager 1.46. """ - UNKNOWN = 0 - NONE = 1 - USER_DISCONNECTED = 2 - DEVICE_DISCONNECTED = 3 - SERVICE_STOPPED = 4 - IP_CONFIG_INVALID = 5 - CONNECT_TIMEOUT = 6 - SERVICE_START_TIMEOUT = 7 - SERVICE_START_FAILED = 8 - NO_SECRETS = 9 - LOGIN_FAILED = 10 - CONNECTION_REMOVED = 11 - DEPENDENCY_FAILED = 12 - DEVICE_REALIZE_FAILED = 13 - DEVICE_REMOVED = 14 + IPVLAN = 34 + """A IPVLAN device. + Since NetworkManager 1.52. + """ -class BluetoothCapabilities(IntFlag): - """Bluetooth Capabilities - Flags: +class DeviceCapabilitiesFlags(IntFlag): + """General device capability flags.""" + + NONE = 0x00000000 + """Device has no special capabilities.""" + SUPPORTED = 0x00000001 + """NetworkManager supports this device.""" + CARRIER_DETECTABLE = 0x00000002 + """This device can indicate carrier status.""" + IS_SOFTWARE = 0x00000004 + """This device is a software device.""" + CAN_SRIOV = 0x00000008 + """This device supports single-root I/O virtualization.""" + + +class WifiCapabilitiesFlags(IntFlag): + """802.11 specific device encryption and authentication flags.""" + + NONE = 0x00000000 + """Device has no encryption/authentication capabilities.""" + CIPHER_WEP40 = 0x00000001 + """Device supports 40/64-bit WEP encryption.""" + CIPHER_WEP104 = 0x00000002 + """Device supports 104/128-bit WEP encryption.""" + CIPHER_TKIP = 0x00000004 + """Device supports TKIP encryption.""" + CIPHER_CCMP = 0x00000008 + """Device supports AES/CCMP encryption.""" + WPA = 0x00000010 + """Device supports WPA1 authentication.""" + WPA2 = 0x00000020 + """Device supports WPA2/RSN authentication.""" + AP = 0x00000040 + """Device supports Access Point mode.""" + ADHOC = 0x00000080 + """Device supports Ad-Hoc mode.""" + FREQ_VALID = 0x00000100 + """Device reports frequency capabilities.""" + FREQ_2GHZ = 0x00000200 + """Device supports 2.4GHz frequencies.""" + FREQ_5GHZ = 0x00000400 + """Device supports 5GHz frequencies.""" + MESH = 0x00001000 + """Device supports acting as a mesh point. + + Since NetworkManager 1.20. + """ + IBSS_WPA2 = 0x2000 + """Device supports WPA2/RSN in an IBSS network. - * NONE - * DIAL_UP - * NETWORK_ACCESS_POINT + Since NetworkManager 1.22. """ - NONE = 0x0 - DIAL_UP = 0x1 - NETWORK_ACCESS_POINT = 0x2 -class IpTunnelMode(IntEnum): - """Mode of IP tunnel - - * UNKNOWN - * IP_IP - * GRE - * SIT - * ISATAP - * VTI - * IP6_IP6 - * IP_IP6 - * IP6_GRE - * VTI6 - * GRE_TAP - * IP6_GRE_TAP +class WifiAccessPointCapabilitiesFlags(IntFlag): + """802.11 access point flags.""" + + NONE = 0x00000000 + """Access point has no special capabilities.""" + PRIVACY = 0x00000001 + """Access point requires authentication and encryption. + + Usually means WEP. """ - UNKNOWN = 0 - IP_IP = 1 - GRE = 2 - SIT = 3 - ISATAP = 4 - VTI = 5 - IP6_IP6 = 6 - IP_IP6 = 7 - IP6_GRE = 8 - VTI6 = 9 - GRE_TAP = 10 - IP6_GRE_TAP = 11 + WPS = 0x00000002 + """Access point supports some WPS method.""" + WPS_BUTTON = 0x00000004 + """Access point supports push-button WPS.""" + WPS_PIN = 0x00000008 + """Access point supports PIN-based WPS.""" -class ModemCapabilities(IntFlag): - """Modem capabilities flags +class WifiAccessPointSecurityFlags(IntFlag): + """802.11 access point security and authentication flags. - Flags: + These flags describe the current security requirements of an access point + as determined from the access point's beacon. + """ - * NONE - * ANALOG_WIRE - * CDMA - * GSM - * LTE + NONE = 0x00000000 + """The access point has no special security requirements.""" + PAIR_WEP40 = 0x00000001 + """40/64-bit WEP is supported for pairwise/unicast encryption.""" + PAIR_WEP104 = 0x00000002 + """104/128-bit WEP is supported for pairwise/unicast encryption.""" + PAIR_TKIP = 0x00000004 + """TKIP is supported for pairwise/unicast encryption.""" + PAIR_CCMP = 0x00000008 + """AES/CCMP is supported for pairwise/unicast encryption.""" + GROUP_WEP40 = 0x00000010 + """40/64-bit WEP is supported for group/broadcast encryption.""" + GROUP_WEP104 = 0x00000020 + """104/128-bit WEP is supported for group/broadcast encryption.""" + GROUP_TKIP = 0x00000040 + """TKIP is supported for group/broadcast encryption.""" + GROUP_CCMP = 0x00000080 + """AES/CCMP is supported for group/broadcast encryption.""" + KEY_MGMT_PSK = 0x00000100 + """WPA/RSN Pre-Shared Key encryption is supported.""" + KEY_MGMT_802_1X = 0x00000200 + """802.1x authentication and key management is supported.""" + KEY_MGMT_SAE = 0x00000400 + """WPA/RSN Simultaneous Authentication of Equals is supported.""" + KEY_MGMT_OWE = 0x00000800 + """WPA/RSN Opportunistic Wireless Encryption is supported.""" + KEY_MGMT_OWE_TM = 0x00001000 + """WPA/RSN Opportunistic Wireless Encryption transition mode is supported. + + Since NetworkManager 1.26. """ - NONE = 0x0 - ANALOG_WIRE = 0x1 - CDMA = 0x2 - GSM = 0x4 - LTE = 0x8 - - -class WirelessCapabilities(IntFlag): - """Wireless device capabilities flags - - Flags: - - * NONE - * CIPHER_WEP40 - * CIPHER_WEP104 - * CIPHER_TKIP - * CIPHER_CCMP - * WPA - * WPA2 - * AP - * ADHOC - * FREQ_VALID - * FREQ_2GHZ - * FREQ_5GHZ - * MESH - * IBSS_WPA2 + KEY_MGMT_EAP_SUITE_B_192 = 0x00002000 + """WPA3 Enterprise Suite-B 192 bit mode is supported. + + Since NetworkManager 1.30. """ - NONE = 0x0 - CIPHER_WEP40 = 0x1 - CIPHER_WEP104 = 0x2 - CIPHER_TKIP = 0x4 - CIPHER_CCMP = 0x8 - WPA = 0x10 - WPA2 = 0x20 - AP = 0x40 - ADHOC = 0x80 - FREQ_VALID = 0x100 - FREQ_2GHZ = 0x200 - FREQ_5GHZ = 0x400 - MESH = 0x800 - IBSS_WPA2 = 0x2000 -class DeviceCapabilities(IntFlag): - """Device Capabilities +class WiFiOperationMode(IntEnum): + """Indicates the 802.11 mode an access point or device is currently in.""" - Flags: + UNKNOWN = 0 + """The device or access point mode is unknown.""" + ADHOC = 1 + """For both devices and access point objects, indicates the object is part + of an Ad-Hoc 802.11 network without a central coordinating access point.""" + INFRA = 2 + """The device or access point is in infrastructure mode. + + For devices, this indicates the device is an 802.11 client/station. For + access point objects, this indicates the object is an access point that + provides connectivity to clients. + """ + AP = 3 + """The device is an access point/hotspot. - * NONE - * SUPPORTED - * CARRIER_DETECTABLE - * IS_SOFTWARE - * CAN_SRIOV + Not valid for access point objects; used only for hotspot mode on the + local machine. + """ + MESH = 4 + """The device is a 802.11s mesh point. + + Since NetworkManager 1.20. """ - NONE = 0x0 - SUPPORTED = 0x1 - CARRIER_DETECTABLE = 0x2 - IS_SOFTWARE = 0x4 - CAN_SRIOV = 0x8 -class DeviceState(IntEnum): - """Device State - - * UNKNOWN - * UNMANAGED - * UNAVAILABLE - * DISCONNECTED - * PREPARE - * CONFIG - * NEED_AUTH - * IP_CONFIG - * IP_CHECK - * SECONDARIES - * ACTIVATED - * DEACTIVATING - * FAILED +class BluetoothCapabilitiesFlags(IntFlag): + """Bluetooth device capabilities.""" + + NONE = 0x00000000 + """Device has no usable capabilities.""" + DUN = 0x00000001 + """Device provides Dial-Up Networking capability.""" + NAP = 0x00000002 + """Device provides Network Access Point capability.""" + + +class ModemCapabilitiesFlags(IntFlag): + """Modem device capabilities. + + Indicates the generic radio access technology families a modem device + supports. For more information on the specific access technologies + the device supports use the ModemManager D-Bus API. + """ + + NONE = 0x00000000 + """Modem has no usable capabilities.""" + POTS = 0x00000001 + """Modem uses the analog wired telephone network and is not a + wireless/cellular device.""" + CDMA_EVDO = 0x00000002 + """Modem supports at least one of CDMA 1xRTT, EVDO revision 0, EVDO + revision A, or EVDO revision B.""" + GSM_UMTS = 0x00000004 + """Modem supports at least one of GSM, GPRS, EDGE, UMTS, HSDPA, HSUPA + or HSPA+ packet switched data capability.""" + LTE = 0x00000008 + """Modem has LTE data capability.""" + SGNR = 0x00000040 + """Modem has 5GNR data capability. + + Since NetworkManager 1.36. """ + + +class WimaxNSPNetworkType(IntEnum): + """WiMAX network type.""" + + UNKNOWN = 0 + """Unknown network type.""" + HOME = 1 + """Home network.""" + PARTNER = 2 + """Partner network.""" + ROAMING_PARTNER = 3 + """Roaming partner network.""" + + +class DeviceState(IntEnum): + """Device's state.""" + UNKNOWN = 0 + """The device's state is unknown.""" UNMANAGED = 10 + """The device is recognized, but not managed by NetworkManager.""" UNAVAILABLE = 20 + """The device is managed by NetworkManager, but is not available for use. + + Reasons may include the wireless switched off, missing firmware, no + ethernet carrier, missing supplicant or modem manager, etc. + """ DISCONNECTED = 30 + """The device can be activated, but is currently idle and not connected + to a network.""" PREPARE = 40 + """The device is preparing the connection to the network. + + This may include operations like changing the MAC address, setting physical + link properties, and anything else required to connect to the requested + network. + """ CONFIG = 50 + """The device is connecting to the requested network. + + This may include operations like associating with the Wi-Fi AP, dialing + the modem, connecting to the remote Bluetooth device, etc. + """ NEED_AUTH = 60 + """The device requires more information to continue connecting to + the requested network. + + This includes secrets like WiFi passphrases, login passwords, PIN + codes, etc. + """ IP_CONFIG = 70 + """The device is requesting IPv4 and/or IPv6 addresses and routing + information from the network.""" IP_CHECK = 80 + """The device is checking whether further action is required for the + requested network connection. + + This may include checking whether only local network access is available, + whether a captive portal is blocking access to the Internet, etc. + """ SECONDARIES = 90 + """The device is waiting for a secondary connection (like a VPN) which + must activated before the device can be activated""" ACTIVATED = 100 + """The device has a network connection, either local or global.""" DEACTIVATING = 110 + """A disconnection from the current network connection was requested, + and the device is cleaning up resources used for that connection. + + The network connection may still be valid. + """ FAILED = 120 + """The device failed to connect to the requested network and + is cleaning up the connection request""" class DeviceStateReason(IntEnum): - """Device State reason - - * NONE - * UNKNOWN - * NOW_MANAGED - * NOW_UNMANAGED - * CONFIG_FAILED - * IP_CONFIG_UNAVAILABLE - * IP_CONFIG_EXPIRED - * NO_SECRETS - * SUPPLICANT_DISCONNECT - * SUPPLICANT_CONFIG_FAILED - * SUPPLICANT_FAILED - * SUPPLICANT_TIMEOUT - * PPP_START_FAILED - * PPP_DISCONNECT - * PPP_FAILED - * DHCP_START_FAILED - * DHCP_ERROR - * DHCP_FAILED - * SHARED_START_FAILED - * SHARED_FAILED - * AUTOIP_START_FAILED - * AUTOIP_ERROR - * AUTOIP_FAILED - * MODEM_BUSY - * MODEM_NO_DIAL_TONE - * MODEM_NO_CARRIER - * MODEM_DIAL_TIMEOUT - * MODEM_DIAL_FAILED - * MODEM_INIT_FAILED - * GSM_APN_FAILED - * GSM_REGISTRATION_NOT_SEARCHING - * GSM_REGISTRATION_DENIED - * GSM_REGISTRATION_TIMEOUT - * GSM_REGISTRATION_FAILED - * GSM_PIN_CHECK_FAILED - * FIRMWARE_MISSING - * REMOVED - * SLEEPING - * CONNECTION_REMOVED - * USER_REQUESTED - * CARRIER - * CONNECTION_ASSUMED - * SUPPLICANT_AVAILABLE - * MODEM_NOT_FOUND - * BT_FAILED - * GSM_SIM_NOT_INSERTED - * GSM_SIM_PIN_REQUIRED - * GSM_SIM_PUK_REQUIRED - * GSM_SIM_WRONG - * INFINIBAND_MODE - * DEPENDENCY_FAILED - * BR2684_FAILED - * MODEM_MANAGER_UNAVAILABLE - * SSID_NOT_FOUND - * SECONDARY_CONNECTION_FAILED - * DCB_FCOE_FAILED - * TEAMD_CONTROL_FAILED - * MODEM_FAILED - * MODEM_AVAILABLE - * SIM_PIN_INCORRECT - * NEW_ACTIVATION - * PARENT_CHANGED - * PARENT_MANAGED_CHANGED - * OVSDB_FAILED - * IP_ADDRESS_DUPLICATE - * IP_METHOD_UNSUPPORTED - * SRIOV_CONFIGURATION_FAILED - * PEER_NOT_FOUND - """ + """Device state change reason codes.""" + + NONE = 0 + """No reason given.""" UNKNOWN = 1 + """Unknown error.""" NOW_MANAGED = 2 + """Device is now managed.""" NOW_UNMANAGED = 3 + """Device is now unmanaged.""" CONFIG_FAILED = 4 + """The device could not be readied for configuration.""" IP_CONFIG_UNAVAILABLE = 5 + """IP configuration could not be reserved. + + No available address, timeout, etc... + """ IP_CONFIG_EXPIRED = 6 + """The IP config is no longer valid.""" NO_SECRETS = 7 + """Secrets were required, but not provided.""" SUPPLICANT_DISCONNECT = 8 + """802.1x supplicant disconnected.""" SUPPLICANT_CONFIG_FAILED = 9 + """802.1x supplicant configuration failed.""" SUPPLICANT_FAILED = 10 + """802.1x supplicant failed.""" SUPPLICANT_TIMEOUT = 11 + """802.1x supplicant took too long to authenticate.""" PPP_START_FAILED = 12 + """PPP service failed to start.""" PPP_DISCONNECT = 13 + """PPP service disconnected.""" PPP_FAILED = 14 + """PPP failed.""" DHCP_START_FAILED = 15 + """DHCP client failed to start.""" DHCP_ERROR = 16 + """DHCP client error.""" DHCP_FAILED = 17 + """DHCP client failed.""" SHARED_START_FAILED = 18 + """Shared connection service failed to start.""" SHARED_FAILED = 19 + """Shared connection service failed.""" AUTOIP_START_FAILED = 20 + """AutoIP service failed to start.""" AUTOIP_ERROR = 21 + """AutoIP service error.""" AUTOIP_FAILED = 22 + """AutoIP service failed.""" MODEM_BUSY = 23 + """The line is busy.""" MODEM_NO_DIAL_TONE = 24 + """No dial tone.""" MODEM_NO_CARRIER = 25 + """No carrier could be established.""" MODEM_DIAL_TIMEOUT = 26 + """The dialing request timed out.""" MODEM_DIAL_FAILED = 27 + """The dialing attempt failed.""" MODEM_INIT_FAILED = 28 + """Modem initialization failed.""" GSM_APN_FAILED = 29 + """Failed to select the specified APN.""" GSM_REGISTRATION_NOT_SEARCHING = 30 + """Not searching for networks.""" GSM_REGISTRATION_DENIED = 31 + """Network registration denied.""" GSM_REGISTRATION_TIMEOUT = 32 + """Network registration timed out.""" GSM_REGISTRATION_FAILED = 33 + """Failed to register with the requested network.""" GSM_PIN_CHECK_FAILED = 34 + """PIN check failed.""" FIRMWARE_MISSING = 35 + """Necessary firmware for the device may be missing.""" REMOVED = 36 + """The device was removed.""" SLEEPING = 37 + """NetworkManager went to sleep.""" CONNECTION_REMOVED = 38 + """The device's active connection disappeared.""" USER_REQUESTED = 39 + """Device disconnected by user or client.""" CARRIER = 40 + """Carrier/link changed.""" CONNECTION_ASSUMED = 41 + """The device's existing connection was assumed.""" SUPPLICANT_AVAILABLE = 42 + """The supplicant is now available.""" MODEM_NOT_FOUND = 43 + """The modem could not be found.""" BT_FAILED = 44 + """The Bluetooth connection failed or timed out.""" GSM_SIM_NOT_INSERTED = 45 + """GSM Modem's SIM Card not inserted.""" GSM_SIM_PIN_REQUIRED = 46 + """GSM Modem's SIM Pin required.""" GSM_SIM_PUK_REQUIRED = 47 + """GSM Modem's SIM Puk required.""" GSM_SIM_WRONG = 48 + """GSM Modem's SIM wrong.""" INFINIBAND_MODE = 49 + """InfiniBand device does not support connected mode.""" DEPENDENCY_FAILED = 50 + """A dependency of the connection failed.""" BR2684_FAILED = 51 + """Problem with the RFC 2684 Ethernet over ADSL bridge.""" MODEM_MANAGER_UNAVAILABLE = 52 + """ModemManager not running.""" SSID_NOT_FOUND = 53 + """The Wi-Fi network could not be found.""" SECONDARY_CONNECTION_FAILED = 54 + """A secondary connection of the base connection failed.""" DCB_FCOE_FAILED = 55 + """DCB or FCoE setup failed.""" TEAMD_CONTROL_FAILED = 56 + """teamd control failed.""" MODEM_FAILED = 57 + """Modem failed or no longer available.""" MODEM_AVAILABLE = 58 + """Modem now ready and available.""" SIM_PIN_INCORRECT = 59 + """SIM PIN was incorrect.""" NEW_ACTIVATION = 60 + """New connection activation was enqueued.""" PARENT_CHANGED = 61 + """The device's parent changed.""" PARENT_MANAGED_CHANGED = 62 + """The device parent's management changed.""" OVSDB_FAILED = 63 + """Problem communicating with Open vSwitch database.""" IP_ADDRESS_DUPLICATE = 64 + """A duplicate IP address was detected.""" IP_METHOD_UNSUPPORTED = 65 + """The selected IP method is not supported.""" SRIOV_CONFIGURATION_FAILED = 66 + """Configuration of SR-IOV parameters failed.""" PEER_NOT_FOUND = 67 + """The Wi-Fi P2P peer could not be found.""" + DEVICE_HANDLER_FAILED = 68 + """The device handler dispatcher returned an error. + Since NetworkManager 1.46. + """ + UNMANAGED_BY_DEFAULT = 69 + """The device is unmanaged because the device type is unmanaged by default. -# Connection Types, e.g. from connecion_profile.connection.type: -# -# There is no central list of all connection types in NM. -# The best bet is to look for nm_connection_is_type() checks which use -# NM_SETTING_(TYPE)_SETTING_NAME #defines (which are fined used for -# settings for this connection-type. One connection_type can have several -# of such settings groups, so we have to filter those to get the strings: -# -# Generated from NetworkManager source using: -# grep -r nm_connection_is_type src/| -# sed -n 's/.*NM_SETTING_/NM_SETTING_/;s/_SETTING_NAME.*/=/p' | -# sort -u >.connection_is_type -# grep -hr define.*_SETTING_NAME src/| -# sed 's/#define //;s/_SETTING_NAME//;s/ /=/' >.setting_defines -# grep -f .connection_is_type .setting_defines | -# sed 's/NM_SETTING_/ /;s/6L/SIXL/;/GENERIC/d;s/=/ = /' -# Manual edit: This resulted in WIRED instead of ETHERNET, but -# ETHERNET is the name used for DeviceType, so use ETHERNET instead to -# be able to lookup connection profiles for Ethernet using DeviceType. -# -# One src/core/nm-device-*.c can support more than one ConnectionType, -# thus there are more ConnectionTypes than DeviceTypes: + Since NetworkManager 1.48. + """ + UNMANAGED_EXTERNAL_DOWN = 70 + """The device is unmanaged because it is an external device and is + unconfigured (down or without addresses). -# From NetworkManager-1.35: -class ConnectionType(str, Enum): - """Connection Types + Since NetworkManager 1.48. + """ + UNMANAGED_LINK_NOT_INIT = 71 + """The device is unmanaged because the link is not initialized by udev. - * ADSL - * BLUETOOTH - * BOND - * BRIDGE - * CDMA - * DUMMY - * ETHERNET - * MODEM - * INFINIBAND - * IP_TUNNEL - * MACSEC - * MACVLAN - * OLPC_MESH - * OVS_BRIDGE - * OVS_INTERFACE - * OVS_INTERFACE - * PPPOE - * SIXLOWPAN - * TEAM - * TUN - * VETH - * VLAN - * VPN - * VRF - * VXLAN - * WIFI_P2P - * WIREGUARD - * WIFI - * WPAN - * LOOPBACK + Since NetworkManager 1.48. """ - ADSL = "adsl" - BLUETOOTH = "bluetooth" - BOND = "bond" - BRIDGE = "bridge" - CDMA = "cdma" - DUMMY = "dummy" - ETHERNET = "802-3-ethernet" - MODEM = "gsm" - INFINIBAND = "infiniband" - IP_TUNNEL = "ip-tunnel" - MACSEC = "macsec" - MACVLAN = "macvlan" - OLPC_MESH = "802-11-olpc-mesh" - OVS_BRIDGE = "ovs-bridge" - OVS_INTERFACE = "ovs-interface" - OVS_PORT = "ovs-port" - PPPOE = "pppoe" - SIXLOWPAN = "6lowpan" - TEAM = "team" - TUN = "tun" - VETH = "veth" - VLAN = "vlan" - VPN = "vpn" - VRF = "vrf" - VXLAN = "vxlan" - WIFI_P2P = "wifi-p2p" - WIREGUARD = "wireguard" - WIFI = "802-11-wireless" - WPAN = "wpan" - LOOPBACK = "loopback" + UNMANAGED_QUITTING = 72 + """The device is unmanaged because NetworkManager is quitting. + Since NetworkManager 1.48. + """ + UNMANAGED_SLEEPING = 73 + """The device is unmanaged because networking is disabled or the system + is suspended. -class DeviceType(IntEnum): - """Device Type + Since NetworkManager 1.48. + """ + UNMANAGED_USER_CONF = 74 + """The device is unmanaged by user decision in NetworkManager.conf. - * UNKNOWN - * ETHERNET - * WIFI - * UNUSED1 - * UNUSED2 - * BLUETOOTH - * OLPC_MESH - * WIMAX - * MODEM - * INFINIBAND - * BOND - * VLAN - * ADSL - * BRIDGE - * GENERIC - * TEAM - * TUN - * IP_TUNNEL - * MACVLAN - * VXLAN - * VETH - * MACSEC - * DUMMY - * PPP - * OVS_INTERFACE - * OVS_PORT - * OVS_BRIDGE - * WPAN - * SIXLOWPAN - * WIREGUARD - * WIFI_P2P - * VRF - * LOOPBACK + Since NetworkManager 1.48. """ - UNKNOWN = 0 - ETHERNET = 1 - WIFI = 2 - UNUSED1 = 3 - UNUSED2 = 4 - BLUETOOTH = 5 - OLPC_MESH = 6 - WIMAX = 7 - MODEM = 8 - INFINIBAND = 9 - BOND = 10 - VLAN = 11 - ADSL = 12 - BRIDGE = 13 - GENERIC = 14 - TEAM = 15 - TUN = 16 - IP_TUNNEL = 17 - MACVLAN = 18 - VXLAN = 19 - VETH = 20 - MACSEC = 21 - DUMMY = 22 - PPP = 23 - OVS_INTERFACE = 24 - OVS_PORT = 25 - OVS_BRIDGE = 26 - WPAN = 27 - SIXLOWPAN = 28 - WIREGUARD = 29 - WIFI_P2P = 30 - VRF = 31 - LOOPBACK = 32 + UNMANAGED_USER_EXPLICIT = 75 + """The device is unmanaged by explicit user decision. + Since NetworkManager 1.48. + """ + UNMANAGED_USER_SETTINGS = 76 + """The device is unmanaged by user decision via settings plugin. + + Since NetworkManager 1.48. + """ + UNMANAGED_USER_UDEV = 77 + """The device is unmanaged via udev rule. + + Since NetworkManager 1.48. + """ -class DeviceMetered(IntEnum): - """Device Metered state - * UNKNOWN - * YES - * NO - * GUESS_YES - * GUESS_NO +class DeviceMetered(IntEnum): + """Device metered state. + + The NMMetered enum has two different purposes: one is to configure + "connection.metered" setting of a ConnectionSettings, and + the other is to express the actual metered state of the Device at a given + moment. + + For the connection profile only UNKNOWN, NO and YES are allowed. + + The device's metered state at runtime is determined by the profile which + is currently active. If the profile explicitly specifies NO or YES, then + the device's metered state is as such. If the connection profile leaves it + undecided at UNKNOWN (the default), then NetworkManager tries to guess + the metered state, for example based on the device type or on DHCP options + (like Android devices exposing a "ANDROID_METERED" DHCP vendor option). + This then leads to either GUESS_NO or GUESS_YES. + + Most applications probably should treat the runtime state + GUESS_YES like YES, and all other states as not metered. + + Note that the per-device metered states are then combined to a global + metered state. This is basically the metered state of the device with the + best default route. However, that generalization of a global metered state + may not be correct if the default routes for IPv4 and IPv6 are on different + devices, or if policy routing is configured. In general, the global metered + state tries to express whether the traffic is likely metered, but since + that depends on the traffic itself, there is not one answer in all cases. + Hence, an application may want to consider the per-device's metered states. """ + UNKNOWN = 0 + """The metered status is unknown.""" YES = 1 + """Metered, the value was explicitly configured.""" NO = 2 + """Not metered, the value was explicitly configured.""" GUESS_YES = 3 + """Metered, the value was guessed.""" GUESS_NO = 4 + """Not metered, the value was guessed.""" -class ConnectivityState(IntEnum): - """Connectivity state +class ConnectionMultiConnect(IntEnum): + """Ability of a connection to be active on multiple devices. + + Since NetworkManager 1.14. + """ + + DEFAULT = 0 + """Indicates that the per-connection setting is unspecified. - * UNKNOWN - * NONE - * PORTAL - * LIMITED - * FULL + In this case, it will fallback to the default value of SINGLE. """ + SINGLE = 1 + """The connection profile can only be active once at each moment. + + Activating a profile that is already active, will first deactivate it. + """ + MANUAL_MULTIPLE = 2 + """The profile can be manually activated multiple times + on different devices. + + However, regarding autoconnect, the profile will autoconnect only if it + is currently not connected otherwise. + """ + MULTIPLE = 3 + """The profile can autoactivate and be manually activated multiple + times together.""" + + +class ActiveConnectionState(IntEnum): + """Indicates the state of an active connection to a specific network.""" + UNKNOWN = 0 + """The state of the connection is unknown.""" + ACTIVATING = 1 + """A network connection is being prepared.""" + ACTIVATED = 2 + """There is a connection to the network.""" + DEACTIVATING = 3 + """The network connection is being torn down and cleaned up.""" + DEACTIVATED = 4 + """The network connection is disconnected and will be removed.""" + + +class ActiveConnectionStateReason(IntEnum): + """Active connection state reasons. + + Since NetworkManager 1.8. + """ + + UNKNOWN = 0 + """The reason for the active connection state change is unknown.""" NONE = 1 - PORTAL = 2 - LIMITED = 3 - FULL = 4 + """No reason was given for the active connection state change.""" + USER_DISCONNECTED = 2 + """The active connection changed state because the user disconnected it.""" + DEVICE_DISCONNECTED = 3 + """The active connection changed state because the device it was using was + disconnected.""" + SERVICE_STOPPED = 4 + """The service providing the VPN connection was stopped.""" + IP_CONFIG_INVALID = 5 + """The IP config of the active connection was invalid.""" + CONNECT_TIMEOUT = 6 + """The connection attempt to the VPN service timed out.""" + SERVICE_START_TIMEOUT = 7 + """A timeout occurred while starting the service providing the + VPN connection.""" + SERVICE_START_FAILED = 8 + """Starting the service providing the VPN connection failed.""" + NO_SECRETS = 9 + """Necessary secrets for the connection were not provided.""" + LOGIN_FAILED = 10 + """Authentication to the server failed.""" + CONNECTION_REMOVED = 11 + """The connection was deleted from settings.""" + DEPENDENCY_FAILED = 12 + """Master connection of this connection failed to activate.""" + DEVICE_REALIZE_FAILED = 13 + """Could not create the software device link.""" + DEVICE_REMOVED = 14 + """The device this connection depended on disappeared.""" -class DeviceInterfaceFlags(IntFlag): - """Device network interface flags +class SecretAgentGetSecretsFlags(IntFlag): + """Flags to modify the behavior of a get_secrets request.""" - Flags: + NONE = 0x0 + """No special behavior. + + By default no user interaction is allowed and requests for secrets + are fulfilled from persistent storage, or if no secrets are available + an error is returned. + """ + ALLOW_INTERACTION = 0x1 + """Allows the request to interact with the user. - * NONE - * UP - * LOWER_UP - * CARRIER + Possibly prompting via UI for secrets if any are required, or if none + are found in persistent storage. + """ + REQUEST_NEW = 0x2 + """Explicitly prompt for new secrets from the user. + + This flag signals that NetworkManager thinks any existing secrets + are invalid or wrong. This flag implies that interaction is allowed. + """ + FLAG_USER_REQUESTED = 0x4 + """Set if the request was initiated by user-requested action via the D-Bus + interface, as opposed to automatically initiated by NetworkManager + in response to (for example) scan results or carrier changes.""" + WPS_PBC_ACTIVE = 0x8 + """Indicates that WPS enrollment is active with PBC method. + + The agent may suggest that the user pushes a button on the router + instead of supplying a PSK. """ + + +class SecretAgentCapabilitiesFlags(IntFlag): + """Secret agent capabilities.""" + NONE = 0x0 + """The agent supports no special capabilities.""" + VPN_HINTS = 0x1 + """The agent supports passing hints to VPN plugin + authentication dialogs.""" + + +class IpTunnelMode(IntEnum): + """Mode of IP tunnel. + + Since NetworkManager 1.2. + """ + + UNKNOWN = 0 + """Unknown/unset tunnel mode.""" + IP_IP = 1 + """IP in IP tunnel.""" + GRE = 2 + """GRE tunnel.""" + SIT = 3 + """SIT tunnel.""" + ISATAP = 4 + """ISATAP tunnel.""" + VTI = 5 + """VTI tunnel.""" + IP6_IP6 = 6 + """IPv6 in IPv6 tunnel.""" + IP_IP6 = 7 + """IPv4 in IPv6 tunnel.""" + IP6_GRE = 8 + """IPv6 GRE tunnel.""" + VTI6 = 9 + """IPv6 VTI tunnel.""" + GRE_TAP = 10 + """GRETAP tunnel.""" + IP6_GRE_TAP = 11 + """IPv6 GRETAP tunnel.""" + + +class CheckpointCreateFlags(IntFlag): + """Flags for checkpoint_create call. + + Since NetworkManager 1.4. + """ + + NONE = 0 + """No flags.""" + DESTROY_ALL = 0x01 + """When creating a new checkpoint, destroy all existing ones.""" + DELETE_NEW_CONNECTIONS = 0x02 + """Upon rollback, delete any new connection added after the checkpoint. + + Since NetworkManager 1.6. + """ + NEW_DEVICES = 0x04 + """Upon rollback, disconnect any new device appeared after the checkpoint. + + Since NetworkManager 1.6. + """ + ALLOW_OVERLAPPING = 0x08 + """By default, creating a checkpoint fails if there are already existing + checkpoints that reference the same devices. With this flag, creation of + such checkpoints is allowed, however, if an older checkpoint that + references overlapping devices gets rolled back, it will automatically + destroy this checkpoint during rollback. This allows to create several + overlapping checkpoints in parallel, and rollback to them at will. With + the special case that rolling back to an older checkpoint will invalidate + all overlapping younger checkpoints. This opts-in that the checkpoint can + be automatically destroyed by the rollback of an older checkpoint. + + Since NetworkManager 1.12. + """ + NO_PRESERVE_EXTERNAL_PORTS = 0x10 + """During rollback, by default externally added ports attached to + bridge devices are preserved. With this flag, the rollback detaches all + external ports. This only has an effect for bridge ports. Before + NetworkManager 1.38, this was the default behavior. + + Since NetworkManager 1.38. + """ + TRACK_INTERNAL_GLOBAL_DNS = 0x20 + """during rollback, by default changes to global DNS via D-BUS interface + are preserved. With this flag, the rollback reverts the global DNS changes + made via D-Bus interface. Global DNS defined in [global-dns] section of + NetworkManager.conf is not impacted by this flag. + + Since NetworkManager 1.48. + """ + + +class CheckpointRollbackResult(IntEnum): + """The result of a checkpoint rollback for a specific device.""" + + OK = 0 + """The rollback succeeded.""" + ERR_NO_DEVICE = 1 + """The device no longer exists.""" + ERR_DEVICE_UNMANAGED = 2 + """The device is now unmanaged.""" + ERR_FAILED = 3 + """Other errors during rollback.""" + + +class SettingsConnectionFlags(IntFlag): + """Settings Connection flags.""" + + NONE = 0 + """An alias for numeric zero, no flags set.""" + UNSAVED = 0x01 + """The connection is not saved to disk. + + That either means, that the connection is in-memory only + and currently is not backed by a file. Or, that the connection is + backed by a file, but has modifications in-memory that were not + persisted to disk. + """ + NM_GENERATED = 0x02 + """A connection is "nm-generated" if it was generated by NetworkManger. + + If the connection gets modified or saved by the user, the flag + gets cleared. A nm-generated is also unsaved and has no backing + file as it is in-memory only. + """ + VOLATILE = 0x04 + """The connection will be deleted when it disconnects. + + That is for in-memory connections (unsaved), which are currently active + but deleted on disconnect. Volatile connections are always unsaved, but + they are also no backing file on disk and are entirely in-memory only. + """ + EXTERNAL = 0x08 + """The profile was generated to represent + an external configuration of a networking device. + + Since NetworkManager 1.26. + """ + + +class ActivationStateFlags(IntFlag): + """Flags describing the current activation state. + + Since NetworkManager 1.10. + """ + + NONE = 0 + """An alias for numeric zero, no flags set.""" + IS_MASTER = 0x1 + """The device is a master.""" + IS_SLAVE = 0x2 + """The device is a slave.""" + LAYER2_READY = 0x4 + """Layer2 is activated and ready.""" + IP4_READY = 0x8 + """IPv4 setting is completed.""" + IP6_READY = 0x10 + """IPv6 setting is completed.""" + MASTER_HAS_SLAVES = 0x20 + """The master has any slave devices attached. + + This only makes sense if the device is a master. + """ + LIFE_TIME_BOUND_TO_PROFILE_VISIBILITY = 0x40 + """The lifetime of the activation is bound to the visibility of + the connection profile, which in turn depends on "connection.permissions" + and whether a session for the user exists. + + Since NetworkManager 1.16. + """ + EXTERNAL = 0x80 + """The active connection was generated to represent an external + configuration of a networking device. + + Since NetworkManager 1.26. + """ + + +class SettingsAddConnection2Flags(IntFlag): + """Flags for the add_connection2() method.""" + + NONE = 0 + """An alias for numeric zero, no flags set.""" + TO_DISK = 0x1 + """To persist the connection to disk.""" + IN_MEMORY = 0x2 + """To make the connection in-memory only.""" + BLOCK_AUTOCONNECT = 0x20 + """Usually, when the connection has autoconnect enabled and gets added, + it becomes eligible to autoconnect right away. Setting this flag, disables + autoconnect until the connection is manually activated.""" + + +class SettingsUpdate2Flags(IntFlag): + """Flags for the update2() method.""" + + NONE = 0 + """An alias for numeric zero, no flags set.""" + TO_DISK = 0x1 + """To persist the connection to disk.""" + IN_MEMORY = 0x2 + """Makes the profile in-memory. + + Note that such profiles are stored in keyfile format under /run. + If the file is already in-memory, the file in /run is updated in-place. + Otherwise, the previous storage for the profile is left unchanged on disk, + and the in-memory copy shadows it. Note that the original filename of the + previous persistent storage (if any) is remembered. That means, when later + persisting the profile again to disk, the file on disk will be overwritten + again. Likewise, when finally deleting the profile, both the storage from + /run and persistent storage are deleted (or if the persistent storage does + not allow deletion, and nmmeta file is written to mark the UUID + as deleted). + """ + IN_MEMORY_DETACHED = 0x4 + """This is almost the same as IN_MEMORY, with one difference: + when later deleting the profile, the original profile will not + be deleted. Instead a nmmeta file is written to /run to indicate + that the profile is gone. Note that if such a nmmeta tombstone + file exists and hides a file in persistent storage, then when + re-adding the profile with the same UUID, then the original storage + is taken over again.""" + IN_MEMORY_ONLY = 0x8 + """This is like IN_MEMORY, but if the connection has a corresponding + file on persistent storage, the file will be deleted right away. + If the profile is later again persisted to disk, a new, unused + filename will be chosen.""" + VOLATILE = 0x10 + """This can be specified with either IN_MEMORY, IN_MEMORY_DETACHED or + IN_MEMORY_ONLY. After making the connection in-memory only, the connection + is marked as volatile. That means, if the connection is currently not + active it will be deleted right away. Otherwise, it is marked to for + deletion once the connection deactivates. A volatile connection cannot + autoactivate again (because it's about to be deleted), but a manual + activation will clear the volatile flag.""" + BLOCK_AUTOCONNECT = 0x20 + """Usually, when the connection has autoconnect enabled and is modified, + it becomes eligible to autoconnect right away. Setting this flag, disables + autoconnect until the connection is manually activated.""" + NO_REAPPLY = 0x40 + """When a profile gets modified that is currently active, then + these changes don't take effect for the active device unless the + profile gets reactivated or the configuration reapplied. There are two + exceptions: by default "connection.zone" and "connection.metered" + properties take effect immediately. Specify this flag to prevent these + properties to take effect, so that the change is restricted to modify + the profile. + + Since NetworkManager 1.20. + """ + + +class DeviceReapplyFlags(IntFlag): + """Flags for the reapply() methodof a device. + + Since NetworkManager 1.42. + """ + + NONE = 0 + """No flag set.""" + PRESERVE_EXTERNAL_IP = 0x1 + """During reapply, preserve external IP addresses and routes.""" + + +class NetworkManagerReloadFlags(IntFlag): + """Flags for the NetworkManager reload() call.""" + + NONE = 0 + """An alias for numeric zero, no flags set. This reloads everything + that is supported and is identical to a SIGHUP.""" + CONF = 0x1 + """Reload the NetworkManager.conf configuration from disk. + + Note that this does not include connections, which can be reloaded + via Setting's reload_connections(). + """ + DNS_RC = 0x2 + """Update DNS configuration, which usually involves + writing /etc/resolv.conf anew.""" + DNS_FULL = 0x4 + """Means to restart the DNS plugin. + + This is for example useful when using dnsmasq plugin, which uses additional + configuration in /etc/NetworkManager/dnsmasq.d. If you edit those files, + you can restart the DNS plugin. This action shortly interrupts name + resolution. + """ + ALL = 0x7 + """All flags.""" + + +class DeviceInterfaceFlags(IntFlag): + """Flags for a network interface. + + Since NetworkManager 1.22. + """ + + NONE = 0 + """An alias for numeric zero, no flags set.""" UP = 0x1 + """The interface is enabled from the administrative point of view. + + Corresponds to kernel IFF_UP. + """ LOWER_UP = 0x2 + """The physical link is up. + + Corresponds to kernel IFF_LOWER_UP. + """ + PROMISC = 0x4 + """Receive all packets. + + Corresponds to kernel IFF_PROMISC. + + Since NetworkManager 1.32. + """ CARRIER = 0x10000 + """The interface has carrier. + In most cases this is equal to the value of LOWER_UP. + However some devices have a non-standard carrier detection mechanism. + """ + LLDP_CLIENT_ENABLED = 0x20000 + """The flag to indicate device LLDP status. -class ConnectionFlags(IntFlag): - """Connection flags + Since NetworkManager 1.32. + """ - Flags - * NONE - * UNSAVED - * GENERATED - * VOLATILE - * EXTERNAL +class ClientPermission(IntEnum): + """Permissions that NetworkManager clients can obtain.""" + + NONE = 0 + """Unknown or no permission.""" + ENABLE_DISABLE_NETWORK = 1 + """Controls whether networking can be globally enabled or disabled.""" + DISABLE_WIFI = 2 + """Controls whether Wi-Fi can be globally enabled or disabled.""" + DISABLE_WWAN = 3 + """Controls whether WWAN (3G) can be globally enabled or disabled.""" + DISABLE_WIMAX = 4 + """Controls whether WiMAX can be globally enabled or disabled.""" + SLEEP_WAKE = 5 + """Controls whether the client can ask NetworkManager to sleep and wake.""" + NETWORK_CONTROL = 6 + """Controls whether networking connections can be started, stopped, and + changed. """ - NONE = 0x0 - UNSAVED = 0x1 - GENERATED = 0x2 - VOLATILE = 0x4 - EXTERNAL = 0x8 + WIFI_SHARE_PROTECTED = 7 + """Controls whether a password protected Wi-Fi hotspot can be created.""" + WIFI_SHARE_OPEN = 8 + """Controls whether an open Wi-Fi hotspot can be created.""" + SETTINGS_MODIFY_SYSTEM = 9 + """Controls whether connections that are available to all users + can be modified. + """ + MODIFY_OWN = 10 + """Controls whether connections owned by the current user + can be modified. + """ + MODIFY_HOSTNAME = 11 + """Controls whether the persistent hostname can be changed.""" + MODIFY_GLOBAL_DNS = 12 + """Modify persistent global DNS configuration.""" + RELOAD = 13 + """Controls access to reload.""" + CHECKPOINT_ROLLBACK = 14 + """Permission to create checkpoints.""" + ENABLE_DISABLE_STATISTICS = 15 + """Controls whether device statistics can be globally enabled + or disabled. + """ + ENABLE_DISABLE_CONNECTIVITY_CHECK = 16 + """Controls whether connectivity check can be enabled or disabled.""" + WIFI_SCAN = 17 + """Controls whether wifi scans can be performed.""" + + +class ClientPermissionResult(IntEnum): + """Indicate what authorizations and permissions required for permission.""" + UNKNOWN = 0 + """Unknown or no authorization.""" + YES = 1 + """Permission is available.""" + AUTH = 2 + """Authorization is necessary before the permission is available.""" + NO = 3 + """Permission to perform the operation is denied by system policy.""" -class VpnState(IntEnum): - """VPN State - * UNKNOWN - * INIT - * SHUTDOWN - * STARTING - * STARTED - * STOPPING - * STOPPED +class RadioFlags(IntFlag): + """Flags related to radio interfaces. + + Since NetworkManager 1.38. + """ + + NONE = 0 + """An alias for numeric zero, no flags set.""" + WLAN_AVAILABLE = 0x1 + """A Wireless LAN device or rfkill switch is detected in the system.""" + WWAN_AVAILABLE = 0x2 + """A Wireless WAN device or rfkill switch is detected in the system.""" + + +class MptcpFlags(IntFlag): + """Flags related to Multi-path TCP. + + Since NetworkManager 1.40. + """ + + NONE = 0 + """The default, meaning that no MPTCP flags are set.""" + DISABLED = 0x1 + """Don't configure MPTCP endpoints on the device.""" + ENABLED = 0x2 + """MPTCP is enabled and endpoints will be configured. + + This flag is implied if any of the other flags indicate that MPTCP is + enabled and therefore in most cases unnecessary. Note that + if "/proc/sys/net/mptcp/enabled" sysctl is disabled, MPTCP handling is + disabled despite this flag. This can be overruled with the + "also-without-sysctl" flag. Note that by default interfaces that don't + have a default route are excluded from having MPTCP endpoints configured. + This can be overruled with the "also-without-default-route" and + this affects endpoints per address family. + """ + ALSO_WITHOUT_SYSCTL = 0x4 + """Even if MPTCP handling is enabled via the "enabled" flag, + it is ignored unless "/proc/sys/net/mptcp/enabled" is on. + With this flag, MPTCP endpoints will be configured regardless + of the sysctl setting.""" + ALSO_WITHOUT_DEFAULT_ROUTE = 0x8 + """Even if MPTCP handling is enabled via the "enabled" flag, + it is ignored per-address family unless NetworkManager + configures a default route. With this flag, NetworkManager will + also configure MPTCP endpoints if there is no default route. + This takes effect per-address family.""" + SIGNAL = 0x10 + """Flag for the MPTCP endpoint. + + The endpoint will be announced/signaled to each peer via an MPTCP + ADD_ADDR sub-option. + """ + SUBFLOW = 0x20 + """Flag for the MPTCP endpoint. + + If additional subflow creation is allowed by the MPTCP limits, the MPTCP + path manager will try to create an additional subflow using this endpoint + as the source address after the MPTCP connection is established. + """ + BACKUP = 0x40 + """Flag for the MPTCP endpoint. + + If this is a subflow endpoint, the subflows created using this endpoint + will have the backup flag set during the connection process. This flag + instructs the peer to only send data on a given subflow when all non-backup + subflows are unavailable. This does not affect outgoing data, where subflow + priority is determined by the backup/non-backup flag received from + the peer. """ + FULLMESH = 0x80 + """Flag for the MPTCP endpoint. + + If this is a subflow endpoint and additional subflow creation is + allowed by the MPTCP limits, the MPTCP path manager will try to create + an additional subflow for each known peer address, using this endpoint as + the source address. This will occur after the MPTCP connection is + established. If the peer did not announce any additional addresses using + the MPTCP ADD_ADDR sub-option, this will behave the same as a plain + subflow endpoint. When the peer does announce addresses, each received + ADD_ADDR sub-option will trigger creation of an additional subflow + to generate a full mesh topology. + """ + + +# From VPN plugin + +class VpnServiceState(IntEnum): + """VPN daemon states.""" + UNKNOWN = 0 + """The state of the VPN plugin is unknown.""" INIT = 1 + """The VPN plugin is initialized.""" SHUTDOWN = 2 + """Not used.""" STARTING = 3 + """The plugin is attempting to connect to a VPN server.""" STARTED = 4 + """The plugin has connected to a VPN server.""" STOPPING = 5 + """The plugin is disconnecting from the VPN server.""" STOPPED = 6 + """The plugin has disconnected from the VPN server.""" -class VpnFailure(IntEnum): - """VPN Failure +class VpnConnectionState(IntEnum): + """VPN connection states.""" - * LOGIN_FAILURE - * CONNECT_FAILED - * BAD_IP_CONFIG - """ - LOGIN_FAILURE = 0 - CONNECT_FAILED = 1 - BAD_IP_CONFIG = 3 - - -class NetworkManagerConnectivityState(IntEnum): - """NetworkManager connectivity state enum + UNKNOWN = 0 + """The state of the VPN connection is unknown.""" + PREPARE = 1 + """The VPN connection is preparing to connect.""" + NEED_AUTH = 2 + """The VPN connection needs authorization credentials.""" + CONNECT = 3 + """The VPN connection is being established.""" + IP_CONFIG_GET = 4 + """The VPN connection is getting an IP address.""" + ACTIVATED = 5 + """The VPN connection is active.""" + FAILED = 6 + """The VPN connection failed.""" + DISCONNECTED = 7 + """The VPN connection is disconnected.""" + + +class VpnConnectionStateReason(IntEnum): + """VPN connection state reasons.""" - * UNKNOWN - * NONE - * PORTAL - * LIMITED - * FULL - """ UNKNOWN = 0 + """The reason for the VPN connection state change is unknown.""" NONE = 1 - PORTAL = 2 - LIMITED = 3 - FULL = 4 + """No reason was given for the VPN connection state change.""" + USER_DISCONNECTED = 2 + """The VPN connection changed state because the user disconnected it.""" + DEVICE_DISCONNECTED = 3 + """The VPN connection changed state because the device it was using + was disconnected.""" + STOPPED = 4 + """The service providing the VPN connection was stopped.""" + IP_CONFIG_INVALID = 5 + """The IP config of the VPN connection was invalid.""" + CONNECT_TIMEOUT = 6 + """The connection attempt to the VPN service timed out.""" + SERVICE_START_TIMEOUT = 7 + """A timeout occurred while starting the service providing + the VPN connection.""" + SERVICE_START_FAILED = 8 + """Starting the service starting the service providing the VPN connection + failed.""" + NO_SECRETS = 9 + """Necessary secrets for the VPN connection were not provided.""" + LOGIN_FAILED = 10 + """Authentication to the VPN server failed.""" + CONNECTION_REMOVED = 11 + """The connection was deleted from settings.""" -class NetworkManagerState(IntEnum): - """NetworkManager state enum +class VpnFailure(IntEnum): + """VPN plugin failure reasons.""" + + LOGIN_FAILED = 0 + """Login failed.""" + CONNECT_FAILED = 1 + """Connect failed.""" + BAD_IP_CONFIG = 2 + """Invalid IP configuration returned from the VPN plugin.""" - * UNKNOWN - * ASLEEP - * DISCONNECTED - * DISCONNECTING - * CONNECTING - * CONNECTED_LOCAL - * CONNECTED_SITE - * GLOBAL +# Connection Types, e.g. from connecion_profile.connection.type: +# +# There is no central list of all connection types in NM. +# The best bet is to look for nm_connection_is_type() checks which use +# NM_SETTING_(TYPE)_SETTING_NAME #defines (which are fined used for +# settings for this connection-type. One connection_type can have several +# of such settings groups, so we have to filter those to get the strings: +# +# Generated from NetworkManager source using: +# grep -r nm_connection_is_type src/| +# sed -n 's/.*NM_SETTING_/NM_SETTING_/;s/_SETTING_NAME.*/=/p' | +# sort -u >.connection_is_type +# grep -hr define.*_SETTING_NAME src/| +# sed 's/#define //;s/_SETTING_NAME//;s/ /=/' >.setting_defines +# grep -f .connection_is_type .setting_defines | +# sed 's/NM_SETTING_/ /;s/6L/SIXL/;/GENERIC/d;s/=/ = /' +# Manual edit: This resulted in WIRED instead of ETHERNET, but +# ETHERNET is the name used for DeviceType, so use ETHERNET instead to +# be able to lookup connection profiles for Ethernet using DeviceType. +# +# One src/core/nm-device-*.c can support more than one ConnectionType, +# thus there are more ConnectionTypes than DeviceTypes: + + +# From NetworkManager-1.35: +class ConnectionType(str, Enum): + """Connection Types. + + * ADSL + * BLUETOOTH + * BOND + * BRIDGE + * CDMA + * DUMMY + * ETHERNET + * MODEM + * INFINIBAND + * IP_TUNNEL + * MACSEC + * MACVLAN + * OLPC_MESH + * OVS_BRIDGE + * OVS_INTERFACE + * OVS_INTERFACE + * PPPOE + * SIXLOWPAN + * TEAM + * TUN + * VETH + * VLAN + * VPN + * VRF + * VXLAN + * WIFI_P2P + * WIREGUARD + * WIFI + * WPAN + * LOOPBACK """ - UNKNOWN = 0 - ASLEEP = 10 - DISCONNECTED = 20 - DISCONNECTING = 30 - CONNECTING = 40 - CONNECTED_LOCAL = 50 - CONNECTED_SITE = 60 - GLOBAL = 70 + + ADSL = "adsl" + BLUETOOTH = "bluetooth" + BOND = "bond" + BRIDGE = "bridge" + CDMA = "cdma" + DUMMY = "dummy" + ETHERNET = "802-3-ethernet" + MODEM = "gsm" + INFINIBAND = "infiniband" + IP_TUNNEL = "ip-tunnel" + MACSEC = "macsec" + MACVLAN = "macvlan" + OLPC_MESH = "802-11-olpc-mesh" + OVS_BRIDGE = "ovs-bridge" + OVS_INTERFACE = "ovs-interface" + OVS_PORT = "ovs-port" + PPPOE = "pppoe" + SIXLOWPAN = "6lowpan" + TEAM = "team" + TUN = "tun" + VETH = "veth" + VLAN = "vlan" + VPN = "vpn" + VRF = "vrf" + VXLAN = "vxlan" + WIFI_P2P = "wifi-p2p" + WIREGUARD = "wireguard" + WIFI = "802-11-wireless" + WPAN = "wpan" + LOOPBACK = "loopback" diff --git a/sdbus_async/networkmanager/interfaces_devices.py b/sdbus_async/networkmanager/interfaces_devices.py index a111eea..e433e42 100644 --- a/sdbus_async/networkmanager/interfaces_devices.py +++ b/sdbus_async/networkmanager/interfaces_devices.py @@ -45,7 +45,8 @@ def name(self) -> str: def bt_capabilities(self) -> int: """Bluetooth device capabilities - See :py:class:`BluetoothCapabilities` + See :py:class:`BluetoothCapabilitiesFlags + `. """ raise NotImplementedError @@ -92,7 +93,8 @@ class NetworkManagerDeviceIPTunnelInterfaceAsync( def mode(self) -> int: """Tunnel mode - See :py:class:`IpTunnelMode` + See :py:class:`IpTunnelMode + `. """ raise NotImplementedError @@ -305,7 +307,8 @@ def modem_capabilities(self) -> int: Switching the radio technology might require firmware reboot. - See :py:class:`ModemCapabilities` + See :py:class:`ModemCapabilitiesFlags + `. """ raise NotImplementedError @@ -707,7 +710,8 @@ def perm_hw_address(self) -> str: def mode(self) -> int: """Operating mode of the device - See :py:class:`WiFiOperationMode` + See :py:class:`WiFiOperationMode + `. """ raise NotImplementedError @@ -730,7 +734,8 @@ def active_access_point(self) -> str: def wireless_capabilities(self) -> int: """List of wireless device capabilities - See :py:class:`WirelessCapabilities` + See :py:class:`WifiCapabilitiesFlags + `. """ raise NotImplementedError @@ -850,7 +855,8 @@ def firmware_version(self) -> str: def capabilities(self) -> int: """Capabilities of the device - See :py:class:`DeviceCapabilities` + See :py:class:`DeviceCapabilitiesFlags + `. """ raise NotImplementedError @@ -858,7 +864,8 @@ def capabilities(self) -> int: def state(self) -> int: """Device state. - See :py:class:`DeviceState` + See :py:class:`DeviceState + `. """ raise NotImplementedError @@ -866,7 +873,10 @@ def state(self) -> int: def state_reason(self) -> Tuple[int, int]: """Current state and the reason. - See :py:class:`DeviceState` and :py:class:`DeviceStateReason` + See :py:class:`DeviceState + ` and + :py:class:`DeviceStateReason + `. """ raise NotImplementedError @@ -938,7 +948,8 @@ def nm_plugin_missing(self) -> bool: def device_type(self) -> int: """Device type - See :py:class:`DeviceType` + See :py:class:`DeviceType + `. """ raise NotImplementedError @@ -961,7 +972,8 @@ def mtu(self) -> int: def metered(self) -> int: """Whether the traffic is subject to limitations - See :py:class:`DeviceMetered` + See :py:class:`DeviceMetered + `. """ raise NotImplementedError @@ -987,7 +999,8 @@ def real(self) -> bool: def ip4_connectivity(self) -> int: """IPv4 connectivity state - See :py:class:`ConnectivityState` + See :py:class:`ConnectivityState + `. """ raise NotImplementedError @@ -995,7 +1008,8 @@ def ip4_connectivity(self) -> int: def ip6_connectivity(self) -> int: """IPv6 connectivity state - See :py:class:`ConnectivityState` + See :py:class:`ConnectivityState + `. """ raise NotImplementedError @@ -1003,7 +1017,8 @@ def ip6_connectivity(self) -> int: def interface_flags(self) -> int: """Interface flags - See :py:class:`DeviceInterfaceFlags` + See :py:class:`DeviceInterfaceFlags + `. """ raise NotImplementedError @@ -1018,7 +1033,10 @@ def state_changed(self) -> Tuple[int, int, int]: Tuple of new state, old state and reason for new state. - See :py:class:`DeviceState` and :py:class:`DeviceStateReason` + See :py:class:`DeviceState + ` and + :py:class:`DeviceStateReason + `. """ raise NotImplementedError diff --git a/sdbus_async/networkmanager/interfaces_other.py b/sdbus_async/networkmanager/interfaces_other.py index 2a4c4af..2a38d67 100644 --- a/sdbus_async/networkmanager/interfaces_other.py +++ b/sdbus_async/networkmanager/interfaces_other.py @@ -41,7 +41,8 @@ class NetworkManagerAccessPointInterfaceAsync( def flags(self) -> int: """Flags describing capabilities of the point - See :py:class:`AccessPointCapabilities` + See :py:class:`WifiAccessPointCapabilitiesFlags + `. """ raise NotImplementedError @@ -49,7 +50,8 @@ def flags(self) -> int: def wpa_flags(self) -> int: """Flags WPA authentication and encryption - See :py:class:`WpaSecurityFlags` + See :py:class:`WifiAccessPointSecurityFlags + `. """ raise NotImplementedError @@ -57,7 +59,8 @@ def wpa_flags(self) -> int: def rsn_flags(self) -> int: """Flags describing RSN (Robust Secure Network) capabilities - See :py:class:`WpaSecurityFlags` + See :py:class:`WifiAccessPointSecurityFlags + `. """ raise NotImplementedError @@ -80,7 +83,8 @@ def hw_address(self) -> str: def mode(self) -> int: """Mode of operation of access point - See :py:class:`WiFiOperationMode` + See :py:class:`WiFiOperationMode + `. """ raise NotImplementedError @@ -127,7 +131,8 @@ async def register_with_capabilities( ) -> None: """Same as register() but with agent capabilities - See :py:class:`SecretAgentCapabilities` + See :py:class:`SecretAgentCapabilitiesFlags + `. """ raise NotImplementedError @@ -202,7 +207,8 @@ def devices(self) -> List[str]: def state(self) -> int: """Connection state - See :py:class:`ConnectionState` + See :py:class:`ActiveConnectionState + `. """ raise NotImplementedError @@ -210,7 +216,8 @@ def state(self) -> int: def state_flags(self) -> int: """Connection state flags - See :py:class:`ConnectionStateFlags` + See :py:class:`ActivationStateFlags + `. """ raise NotImplementedError @@ -270,7 +277,10 @@ def master(self) -> str: def state_changed(self) -> Tuple[int, int]: """Signal of the new state and the reason - See :py:class:`ConnectionState` and :py:class:`ConnectionStateReason` + See :py:class:`ActiveConnectionState + ` + and :py:class:`ActiveConnectionStateReason + `. """ raise NotImplementedError @@ -284,7 +294,8 @@ class NetworkManagerVPNConnectionInterfaceAsync( def vpn_state(self) -> int: """VPN connection state - See :py:class:`ConnectionState` + See :py:class:`VpnConnectionState + `. """ raise NotImplementedError @@ -298,7 +309,10 @@ def vpn_state_changed(self) -> Tuple[int, int]: """Signal when VPN state changed Tuple of new state and reason. - See :py:class:`ConnectionState` and :py:class:`ConnectionStateReason` + See :py:class:`VpnConnectionState + ` + and :py:class:`VpnConnectionStateReason + `. """ raise NotImplementedError @@ -595,7 +609,8 @@ def unsaved(self) -> bool: def flags(self) -> int: """Connection flags - See :py:class:`ConnectionFlags` + See :py:class:`SettingsConnectionFlags + `. """ raise NotImplementedError @@ -909,7 +924,8 @@ async def new_secrets( def state(self) -> int: """VPN state - See :py:class:`VpnState` + See :py:class:`VpnServiceState + `. """ raise NotImplementedError @@ -917,7 +933,8 @@ def state(self) -> int: def state_changed(self) -> int: """Signal when VPN state changed with new VPN state. - See :py:class:`VpnState` + See :py:class:`VpnServiceState + `. """ raise NotImplementedError @@ -950,7 +967,8 @@ def login_banner(self) -> str: def failure(self) -> int: """Signal when VPN failure occurs - See :py:class:`VpnFailure` + See :py:class:`VpnFailure + `. """ raise NotImplementedError @@ -969,7 +987,8 @@ def name(self) -> str: def flags(self) -> int: """Flags describing capabilities of the point - See :py:class:`AccessPointCapabilities` + See :py:class:`WifiAccessPointCapabilitiesFlags + `. """ raise NotImplementedError @@ -1024,14 +1043,13 @@ async def reload( ) -> None: """Reload NetworkManager configuration - Flags control what to reload: + Flags control what to reload. - * 0x0 everything - * 0x1 NetworkManager.conf - * 0x2 DNS configuration - * 0x4 Restart DNS plugin + Use :py:class:`NetworkManagerReloadFlags + ` + to create flags. - :param flags: Reload what? + :param flags: What to reload. """ raise NotImplementedError @@ -1166,7 +1184,8 @@ async def check_connectivity( ) -> int: """Get current connectivity state - See :py:class:`NetworkManagerConnectivityState` + See :py:class:`ConnectivityState + `. """ raise NotImplementedError @@ -1179,7 +1198,8 @@ async def get_state( ) -> int: """Get current NetworkManager state - See :py:class:`NetworkManagerState` + See :py:class:`NetworkManagerState + `. """ raise NotImplementedError @@ -1290,7 +1310,8 @@ def primary_connection_type(self) -> str: def metered(self) -> int: """Primary connection metered status - See :py:class:`DeviceMetered` + See :py:class:`DeviceMetered + `. """ raise NotImplementedError @@ -1322,7 +1343,8 @@ def capabilities(self) -> List[int]: def state(self) -> int: """Overall state of NetworkManager - See :py:class:`NetworkManagerState` + See :py:class:`NetworkManagerState + `. """ raise NotImplementedError @@ -1330,7 +1352,8 @@ def state(self) -> int: def connectivity(self) -> int: """Overall state of connectivity - See :py:class:`NetworkManagerConnectivityState` + See :py:class:`ConnectivityState + `. """ raise NotImplementedError @@ -1365,7 +1388,8 @@ def check_permissions(self) -> None: def state_changed(self) -> int: """NetworkManager state changed - See :py:class:`NetworkManagerState` + See :py:class:`NetworkManagerState + `. """ raise NotImplementedError diff --git a/sdbus_async/networkmanager/objects.py b/sdbus_async/networkmanager/objects.py index e03c69a..f5ae24e 100644 --- a/sdbus_async/networkmanager/objects.py +++ b/sdbus_async/networkmanager/objects.py @@ -159,6 +159,7 @@ def __init__(self, bus: Optional[SdBus] = None) -> None: NETWORK_MANAGER_SERVICE_NAME, '/org/freedesktop/NetworkManager/Settings', bus) + self._nm_used_bus = bus async def get_connections_by_id(self, connection_id: str) -> List[str]: """Helper method to get a list of connection profile paths @@ -171,7 +172,7 @@ async def get_connections_by_id(self, connection_id: str) -> List[str]: connection_paths_with_matching_id = [] connection_paths: List[str] = await self.connections for connection_path in connection_paths: - settings = NetworkConnectionSettings(connection_path) + settings = NetworkConnectionSettings(connection_path, self._nm_used_bus) settings_properites = await settings.get_settings() # settings_properites["connection"]["id"][1] gives the id value: if settings_properites["connection"]["id"][1] == connection_id: diff --git a/sdbus_async/networkmanager/settings/datatypes.py b/sdbus_async/networkmanager/settings/datatypes.py index 278012c..a7ec83a 100644 --- a/sdbus_async/networkmanager/settings/datatypes.py +++ b/sdbus_async/networkmanager/settings/datatypes.py @@ -118,7 +118,7 @@ class WireguardPeers(NetworkManagerSettingsMixin): metadata={'dbus_name': 'public-key', 'dbus_type': 's'}, default=None, ) - endpoint: Optional[int] = field( + endpoint: Optional[str] = field( metadata={'dbus_name': 'endpoint', 'dbus_type': 's'}, default=None, ) @@ -126,6 +126,10 @@ class WireguardPeers(NetworkManagerSettingsMixin): metadata={'dbus_name': 'allowed-ips', 'dbus_type': 'as'}, default=None, ) + persistent_keepalive: Optional[int] = field( + metadata={"dbus_name": "persistent-keepalive", "dbus_type": "u"}, + default=None, + ) @dataclass diff --git a/sdbus_block/networkmanager/__init__.py b/sdbus_block/networkmanager/__init__.py index dbd8dcf..43393af 100644 --- a/sdbus_block/networkmanager/__init__.py +++ b/sdbus_block/networkmanager/__init__.py @@ -20,30 +20,46 @@ from __future__ import annotations from .enums import ( - AccessPointCapabilities, - BluetoothCapabilities, - ConnectionFlags, - ConnectionState, - ConnectionStateFlags, - ConnectionStateReason, + ActivationStateFlags, + ActiveConnectionState, + ActiveConnectionStateReason, + BluetoothCapabilitiesFlags, + CheckpointCreateFlags, + CheckpointRollbackResult, + ClientPermission, + ClientPermissionResult, + ConnectionMultiConnect, ConnectionType, ConnectivityState, - DeviceCapabilities, + DeviceCapabilitiesFlags, DeviceInterfaceFlags, DeviceMetered, + DeviceReapplyFlags, DeviceState, DeviceStateReason, DeviceType, IpTunnelMode, - ModemCapabilities, - NetworkManagerConnectivityState, + ModemCapabilitiesFlags, + MptcpFlags, + NetworkManagerCapabilitiesFlags, + NetworkManagerReloadFlags, NetworkManagerState, - SecretAgentCapabilities, + NetworkManagerVersionInfoCapability, + RadioFlags, + SecretAgentCapabilitiesFlags, + SecretAgentGetSecretsFlags, + SettingsAddConnection2Flags, + SettingsConnectionFlags, + SettingsUpdate2Flags, + VpnConnectionState, + VpnConnectionStateReason, VpnFailure, - VpnState, + VpnServiceState, + WifiAccessPointCapabilitiesFlags, + WifiAccessPointSecurityFlags, + WifiCapabilitiesFlags, WiFiOperationMode, - WirelessCapabilities, - WpaSecurityFlags, + WimaxNSPNetworkType, ) from .exceptions import ( NetworkManagerAlreadyAsleepOrAwakeError, @@ -218,30 +234,46 @@ __all__ = ( # .enums - 'AccessPointCapabilities', - 'BluetoothCapabilities', - 'ConnectionFlags', - 'ConnectionState', - 'ConnectionStateFlags', - 'ConnectionStateReason', + 'ActivationStateFlags', + 'ActiveConnectionState', + 'ActiveConnectionStateReason', + 'BluetoothCapabilitiesFlags', + 'CheckpointCreateFlags', + 'CheckpointRollbackResult', + 'ClientPermission', + 'ClientPermissionResult', + 'ConnectionMultiConnect', 'ConnectionType', 'ConnectivityState', - 'DeviceCapabilities', + 'DeviceCapabilitiesFlags', 'DeviceInterfaceFlags', 'DeviceMetered', + 'DeviceReapplyFlags', 'DeviceState', 'DeviceStateReason', 'DeviceType', 'IpTunnelMode', - 'ModemCapabilities', - 'NetworkManagerConnectivityState', + 'ModemCapabilitiesFlags', + 'MptcpFlags', + 'NetworkManagerCapabilitiesFlags', + 'NetworkManagerReloadFlags', 'NetworkManagerState', - 'SecretAgentCapabilities', + 'NetworkManagerVersionInfoCapability', + 'RadioFlags', + 'SecretAgentCapabilitiesFlags', + 'SecretAgentGetSecretsFlags', + 'SettingsAddConnection2Flags', + 'SettingsConnectionFlags', + 'SettingsUpdate2Flags', + 'VpnConnectionState', + 'VpnConnectionStateReason', 'VpnFailure', - 'VpnState', + 'VpnServiceState', + 'WifiAccessPointCapabilitiesFlags', + 'WifiAccessPointSecurityFlags', + 'WifiCapabilitiesFlags', 'WiFiOperationMode', - 'WirelessCapabilities', - 'WpaSecurityFlags', + 'WimaxNSPNetworkType', # .exceptions 'NetworkManagerAlreadyAsleepOrAwakeError', 'NetworkManagerAlreadyEnabledOrDisabledError', diff --git a/sdbus_block/networkmanager/objects.py b/sdbus_block/networkmanager/objects.py index 3d4c68d..ef48e29 100644 --- a/sdbus_block/networkmanager/objects.py +++ b/sdbus_block/networkmanager/objects.py @@ -154,6 +154,7 @@ def __init__(self, bus: Optional[SdBus] = None) -> None: NETWORK_MANAGER_SERVICE_NAME, '/org/freedesktop/NetworkManager/Settings', bus) + self._nm_used_bus = bus def get_connections_by_id(self, connection_id: str) -> List[str]: """Helper method to get a list of connection profile paths @@ -165,7 +166,7 @@ def get_connections_by_id(self, connection_id: str) -> List[str]: """ connection_paths_with_matching_id = [] for connection_path in self.connections: - profile = NetworkConnectionSettings(connection_path) + profile = NetworkConnectionSettings(connection_path, self._nm_used_bus) # profile.get_settings()["connection"]["id"][1] gives the id value: if profile.get_settings()["connection"]["id"][1] == connection_id: connection_paths_with_matching_id.append(connection_path) diff --git a/setup.py b/setup.py index 85c82fe..02c1185 100644 --- a/setup.py +++ b/setup.py @@ -30,15 +30,15 @@ long_description=long_description, long_description_content_type='text/markdown', version='2.0.0', - url='https://github.com/igo95862/python-sdbus', + url='https://github.com/python-sdbus/python-sdbus-networkmanager', author='igo95862', author_email='igo95862@yandex.ru', license='LGPL-2.1-or-later', keywords='dbus networkmanager networking linux freedesktop', project_urls={ - 'Documentation': 'https://python-sdbus.readthedocs.io/en/latest/', - 'Source': 'https://github.com/igo95862/python-sdbus/', - 'Tracker': 'https://github.com/igo95862/python-sdbus/issues/', + 'Documentation': 'https://python-sdbus-networkmanager.readthedocs.io/en/latest/', + 'Source': 'https://github.com/python-sdbus/python-sdbus-networkmanager/', + 'Tracker': 'https://github.com/python-sdbus/python-sdbus-networkmanager/issues/', }, classifiers=[ 'Development Status :: 4 - Beta',