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 new file mode 100644 index 0000000..8435887 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,92 @@ +## 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 + +`connection_profile` of `NetworkConnectionSettings` object has been replaced with +equivalent `get_profile` method which can also fetch the secrets fields. (you can +use `mypy` to check) + +### Breaking changes + +* Renamed certain elements of `ConnectionType` enum to match `DeviceType` enum. + + * `WIRED` -> `ETHERNET` + * `GSM` -> `MODEM` + +### Features + +* Added connection settings dataclasses. + Those dataclasses are found under `networkmanager.settings` sub-package. + They allow for easy and typed reading, modifying and writing connection settings + without dealing with D-Bus variants. + + Thank you @bernhardkaindl for spearheading this feature. + + New methods have been added to existing interfaces that utilize the new dataclasses: + + * `NetworkManagerSettingsConnectionInterface` + + * `get_profile` + * `update_profile` + + * `NetworkManagerSettingsInterface` + + * `add_connection_profile` + + * `NetworkManagerInterfaceAsync` + + * `add_and_activate_connection_profile` + +* Added support for loopback devices from NetworkManager 1.42 + +## 1.1.0 + +### Features + +* Added NetworkManager errors as named exceptions. + +## 1.0.0 + +Initial release. 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/device_interfaces.rst b/docs/device_interfaces.rst index ab5f00f..0078390 100644 --- a/docs/device_interfaces.rst +++ b/docs/device_interfaces.rst @@ -77,3 +77,6 @@ Contains interfaces for most types of devices. .. autoclass:: sdbus_async.networkmanager.NetworkManagerPPPInterfaceAsync :members: + +.. autoclass:: sdbus_async.networkmanager.NetworkManagerLoopbackInterfaceAsync + :members: 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 c709bf2..3003952 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,16 +4,23 @@ Network Manager binds for python-sdbus This package contains python-sdbus for D-Bus interface of `NetworkManager `_. +.. note:: + See `upstream documentation for NetworkManager D-Bus API + `_ + for more detailed documentation. + .. toctree:: :maxdepth: 3 :caption: Contents: + quickstart objects examples device_interfaces other_interfaces + profile_settings enums exceptions -See `python-sdbus `_ homepage if you are +See `python-sdbus `_ homepage if you are unfamiliar with python-sdbus. diff --git a/docs/objects.rst b/docs/objects.rst index 75c6da7..e113adb 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -97,6 +97,9 @@ Device objects .. autoclass:: sdbus_async.networkmanager.NetworkDevicePPP :members: +.. autoclass:: sdbus_async.networkmanager.NetworkDeviceLoopback + :members: + Connection configuration objects -------------------------------- diff --git a/docs/profile_settings.rst b/docs/profile_settings.rst new file mode 100644 index 0000000..c2f3873 --- /dev/null +++ b/docs/profile_settings.rst @@ -0,0 +1,159 @@ +Connection Profile Settings Helpers +=================================== + +.. autoclass:: sdbus_async.networkmanager.settings.AdslSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.BluetoothSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.BondSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.BondPortSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.BridgeSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.BridgePortSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.CdmaSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.ConnectionSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.DcbSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.DummySettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.EapolSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.EthernetSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.EthtoolSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.GenericSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.GsmSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.HostnameSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.InfinibandSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.IpTunnelSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.Ipv4Settings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.Ipv6Settings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.LowpanSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.MacsecSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.MacvlanSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.MatchSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OlpcMeshSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OvsBridgeSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OvsDpdkSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OvsExternalIdsSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OvsInterfaceSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OvsPatchSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.OvsPortSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.PppSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.PppoeSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.ProxySettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.SerialSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.SriovSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.TcSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.TeamSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.TeamPortSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.TunSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.UserSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.VethSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.VlanSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.VpnSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.VrfSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.VxlanSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.WifiP2PSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.WimaxSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.WireguardSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.WirelessSettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.WirelessSecuritySettings + :members: + +.. autoclass:: sdbus_async.networkmanager.settings.WpanSettings + :members: + 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/2.0.0/async/add-eth-connection-async.py b/examples/2.0.0/async/add-eth-connection-async.py new file mode 100755 index 0000000..f7be36f --- /dev/null +++ b/examples/2.0.0/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/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 71% rename from examples/async/add-wifi-psk-connection-async.py rename to examples/2.0.0/async/add-wifi-psk-connection-async.py index c831577..fb9022a 100755 --- a/examples/async/add-wifi-psk-connection-async.py +++ b/examples/2.0.0/async/add-wifi-psk-connection-async.py @@ -3,7 +3,7 @@ # # Example to create a new WiFi-PSK network connection profile: # -# $ add-wifi-psk-connection.py --help +# $ 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] # @@ -12,6 +12,7 @@ # 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 @@ -46,16 +47,19 @@ import asyncio import binascii import functools +import logging import sdbus -import uuid +from uuid import uuid4 from argparse import ArgumentParser, Namespace from passlib.utils.pbkdf2 import pbkdf2 # type: ignore -from pprint import pprint +from pprint import pformat from sdbus_async.networkmanager import ( NetworkManagerSettings as SettingsManager, + ConnectionType, +) +from sdbus_async.networkmanager.settings import ( ConnectionProfile, ConnectionSettings, - ConnectionType, Ipv4Settings, Ipv6Settings, WirelessSettings, @@ -63,14 +67,18 @@ ) -async def add_wifi_psk_connection_profile_async(args: Namespace) -> None: - """Add a temporary (not yet saved) network connection profile""" +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 + 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: @@ -79,10 +87,10 @@ async def add_wifi_psk_connection_profile_async(args: Namespace) -> None: profile = ConnectionProfile( connection=ConnectionSettings( - uuid=str(uuid.uuid4()), - connection_type=ConnectionType.WIFI, connection_id=args.conn_id, - autoconnect=args.auto, + 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"), @@ -93,25 +101,27 @@ async def add_wifi_psk_connection_profile_async(args: Namespace) -> None: ) # To bind the new connection to a specific interface, use this: - if args.interface_name: + if hasattr(args, "interface_name") and args.interface_name: profile.connection.interface_name = args.interface_name - networkmanager_settings = SettingsManager() - if args.save: - await networkmanager_settings.add_connection(profile.to_dbus()) - print("New connection profile created and saved, show it with:") - else: - await networkmanager_settings.add_connection_unsaved(profile.to_dbus()) - print("New unsaved connection profile created, show it with:") - print(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') - print("Settings used:") - functools.partial(pprint, sort_dicts=False)(profile.to_settings_dict()) + 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") @@ -120,4 +130,8 @@ async def add_wifi_psk_connection_profile_async(args: Namespace) -> None: p.add_argument("--save", dest="save", action="store_true", help="Save") args = p.parse_args() sdbus.set_default_bus(sdbus.sd_bus_open_system()) - asyncio.run(add_wifi_psk_connection_profile_async(args)) + 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/2.0.0/async/delete-connection-by-uuid-async.py b/examples/2.0.0/async/delete-connection-by-uuid-async.py new file mode 100755 index 0000000..ac1ee3d --- /dev/null +++ b/examples/2.0.0/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/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 97% rename from examples/async/list-connections-async.py rename to examples/2.0.0/async/list-connections-async.py index 4b8bfb9..cd39131 100755 --- a/examples/async/list-connections-async.py +++ b/examples/2.0.0/async/list-connections-async.py @@ -36,7 +36,7 @@ async def list_connection_profiles_async() -> None: async def print_connection_profile(connection_path: str) -> None: connectionsettings_service = NetworkConnectionSettings(connection_path) - profile = await connectionsettings_service.connection_profile() + profile = await connectionsettings_service.get_profile() connection = profile.connection print("-------------------------------------------------------------") print("name:", connection.connection_id) 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/2.0.0/async/netdevinfo-async.py b/examples/2.0.0/async/netdevinfo-async.py new file mode 100755 index 0000000..e4ef140 --- /dev/null +++ b/examples/2.0.0/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/async/update-connection-async.py b/examples/2.0.0/async/update-connection-async.py similarity index 87% rename from examples/async/update-connection-async.py rename to examples/2.0.0/async/update-connection-async.py index 2323d07..d838bda 100755 --- a/examples/async/update-connection-async.py +++ b/examples/2.0.0/async/update-connection-async.py @@ -3,7 +3,7 @@ # # Update a property of a connection profile, looked up by connection id # -# This version uses connection_manager.connection_profile().to_settings_dict() +# 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: @@ -17,9 +17,9 @@ import asyncio import sdbus from functools import partial -from sdbus_async.networkmanager import ConnectionProfile 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 @@ -27,16 +27,16 @@ 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 recieved id + # 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 connecttion with given id + # Get the profile settings of the first connection with given id connection_manager = NetworkConnectionSettings(connection_paths[0]) - existing_connection_profile = await connection_manager.connection_profile() + 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 diff --git a/examples/block/add-eth-connection.py b/examples/2.0.0/block/add-eth-connection.py old mode 100644 new mode 100755 similarity index 55% rename from examples/block/add-eth-connection.py rename to examples/2.0.0/block/add-eth-connection.py index c8828af..b692890 --- a/examples/block/add-eth-connection.py +++ b/examples/2.0.0/block/add-eth-connection.py @@ -3,15 +3,16 @@ # # Example to create a new Ethernet network connection profile: # -# add-eth-connection.py --help -# usage: add-eth-connection.py [-h] [-c CONN_ID] [-i INTERFACE_NAME] [-4 IP4] -# [-g GW] [-a] [--save] +# 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 @@ -23,28 +24,33 @@ import sdbus import functools +import logging import pprint import sys -import uuid +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_profile(args: Namespace) -> None: - """Add a temporary (not yet saved) network connection profile""" +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): - print(f'Connections using ID "{args.conn_id}" exist, remove them:') - print(f'Run: nmcli connection delete "{args.conn_id}"') - return + 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("/") - profile: NetworkManagerConnectionProperties = { + properties: NetworkManagerConnectionProperties = { "connection": { "id": ("s", args.conn_id), - "uuid": ("s", str(uuid.uuid4())), + "uuid": ("s", str(args.uuid)), "type": ("s", "802-3-ethernet"), "autoconnect": ("b", args.auto), }, @@ -63,28 +69,30 @@ def add_ethernet_connection_profile(args: Namespace) -> None: "ipv6": {"method": ("s", "disabled")}, } if args.interface_name: - profile["connection"]["interface-name"] = ("s", 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("/") - profile["ipv4"]["gateway"] = ("s", default_gateway) - profile["ipv4"]["route-metric"] = ("u", int(route_metric)) + properties["ipv4"]["gateway"] = ("s", default_gateway) + properties["ipv4"]["route-metric"] = ("u", int(route_metric)) - if args.save: - NetworkManagerSettings().add_connection(profile) - print("New connection profile created and saved, show it with:") - else: - NetworkManagerSettings().add_connection_unsaved(profile) - print("New unsaved connection profile created, show it with:") + 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" - print(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') - print("Settings used:") - functools.partial(pprint.pprint, sort_dicts=False)(profile) + 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") @@ -92,4 +100,8 @@ def add_ethernet_connection_profile(args: Namespace) -> None: p.add_argument("--save", dest="save", action="store_true", help="Save") args = p.parse_args() sdbus.set_default_bus(sdbus.sd_bus_open_system()) - add_ethernet_connection_profile(args) + 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/block/add-wifi-psk-connection.py b/examples/2.0.0/block/add-wifi-psk-connection.py similarity index 72% rename from examples/block/add-wifi-psk-connection.py rename to examples/2.0.0/block/add-wifi-psk-connection.py index e80e2de..3b732be 100755 --- a/examples/block/add-wifi-psk-connection.py +++ b/examples/2.0.0/block/add-wifi-psk-connection.py @@ -3,7 +3,7 @@ # # Example to create a new WiFi-PSK network connection profile: # -# $ add-wifi-psk-connection.py --help +# $ 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] # @@ -12,6 +12,7 @@ # 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 @@ -44,29 +45,34 @@ # -> org.freedesktop.NetworkManager.Settings (Settings Profile Manager) import functools +import logging import pprint import sdbus -import uuid +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_profile(args: Namespace) -> None: - """Add a temporary (not yet saved) network connection profile""" +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 + return "" properties: NetworkManagerConnectionProperties = { "connection": { "id": ("s", args.conn_id), - "uuid": ("s", str(uuid.uuid4())), + "uuid": ("s", str(args.uuid)), "type": ("s", "802-11-wireless"), - "autoconnect": ("b", args.auto), + "autoconnect": ("b", bool(hasattr(args, "auto") and args.auto)), }, "802-11-wireless": { "mode": ("s", "infrastructure"), @@ -83,25 +89,27 @@ def add_wifi_psk_connection_profile(args: Namespace) -> None: } # To bind the new connection to a specific interface, use this: - if args.interface_name: + if hasattr(args, "interface_name") and args.interface_name: properties["connection"]["interface-name"] = ("s", args.interface_name) - if args.save: - NetworkManagerSettings().add_connection(properties) - print("New connection profile created and saved, show it with:") - else: - NetworkManagerSettings().add_connection_unsaved(properties) - print("New unsaved connection profile created, show it with:") - - print(f'nmcli connection show "{args.conn_id}"|grep -v -e -- -e default') - print("Settings used:") - functools.partial(pprint.pprint, sort_dicts=False)(properties) + 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") @@ -109,4 +117,8 @@ def add_wifi_psk_connection_profile(args: Namespace) -> None: p.add_argument("--save", dest="save", action="store_true", help="Save") args = p.parse_args() sdbus.set_default_bus(sdbus.sd_bus_open_system()) - add_wifi_psk_connection_profile(args) + 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/2.0.0/block/delete-connection-by-uuid.py b/examples/2.0.0/block/delete-connection-by-uuid.py new file mode 100755 index 0000000..2b42509 --- /dev/null +++ b/examples/2.0.0/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/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/2.0.0/block/list-connections.py b/examples/2.0.0/block/list-connections.py new file mode 100755 index 0000000..f8d262a --- /dev/null +++ b/examples/2.0.0/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/2.0.0/block/netdevinfo.py b/examples/2.0.0/block/netdevinfo.py new file mode 100755 index 0000000..1dec341 --- /dev/null +++ b/examples/2.0.0/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/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/async/netdevinfo-async.py b/examples/async/netdevinfo-async.py deleted file mode 100755 index 8f3a6ff..0000000 --- a/examples/async/netdevinfo-async.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/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 ( - NetworkManager, - NetworkDeviceGeneric, - IPv4Config, - DeviceType, - NetworkDeviceWireless, - WiFiOperationMode, - AccessPoint, -) -from typing import Any, Dict, List, Tuple -NetworkManagerAddressData = List[Dict[str, Tuple[str, Any]]] - - -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 - - ip4_conf = IPv4Config(device_ip4_conf_path) - gateway: str = await ip4_conf.gateway - - print("Device: ", await generic_device.interface) - 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 await generic_device.device_type == DeviceType.WIFI: - wifi = NetworkDeviceWireless(device_path) - print("Wifi: ", WiFiOperationMode(await wifi.mode).name.title()) - ap = AccessPoint(await wifi.active_access_point) - ssid: bytes = await ap.ssid - if ssid: - print("SSID: ", ssid.decode("utf-8", "ignore")) - if await ap.strength: - print("Signal: ", await ap.strength) - - print("") - - -if __name__ == "__main__": - sdbus.set_default_bus(sdbus.sd_bus_open_system()) - asyncio.run(list_networkdevice_details_async()) diff --git a/examples/block/list-connections.py b/examples/block/list-connections.py deleted file mode 100755 index 52e1c4d..0000000 --- a/examples/block/list-connections.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/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 ( - NetworkManagerSettings, - NetworkConnectionSettings, -) - - -def list_connection_profiles_blocking() -> None: - for connection_path in NetworkManagerSettings().connections: - connection_settings = NetworkConnectionSettings(connection_path) - settings = connection_settings.get_settings() - connection = settings["connection"] - - # Skip connection profiles for bridges and wireless networks - if connection["type"][1] in ("bridge", "802-11-wireless"): - continue - - print("-------------------------------------------------------------") - print("name:", connection["id"][1]) - print("uuid:", connection["uuid"][1]) - print("type:", connection["type"][1]) - if "interface-name" in connection: - print(" interface-name:", connection["interface-name"][1]) - - if "ipv4" in settings: - ipv4 = settings["ipv4"] - print("ipv4: method:", ipv4["method"][1]) - if "address-data" in ipv4: - for a in ipv4["address-data"][1]: - print(f' ipaddr: {a["address"][1]}/{a["prefix"][1]}') - if "route-metric" in ipv4: - print(f' route-metric: {ipv4["route-metric"][1]}') - - if "ipv6" in settings: - print("ipv6: method:", settings["ipv6"]["method"][1]) - - -if __name__ == "__main__": - sdbus.set_default_bus(sdbus.sd_bus_open_system()) - list_connection_profiles_blocking() diff --git a/examples/block/netdevinfo.py b/examples/block/netdevinfo.py deleted file mode 100755 index 5004067..0000000 --- a/examples/block/netdevinfo.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/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 ( - NetworkManager, - NetworkDeviceGeneric, - IPv4Config, - DeviceType, - NetworkDeviceWireless, - WiFiOperationMode, - AccessPoint, -) - - -def list_networkdevice_details_blocking() -> None: - nm = NetworkManager() - devices_paths = nm.get_devices() - - for device_path in devices_paths: - generic_device = NetworkDeviceGeneric(device_path) - device_ip4_conf_path = generic_device.ip4_config - if device_ip4_conf_path == "/": - continue - - ip4_conf = IPv4Config(device_ip4_conf_path) - - print("Device: ", generic_device.interface) - if ip4_conf.gateway: - print("Gateway:", ip4_conf.gateway) - - for ip4addr in ip4_conf.address_data: - print(f'Address: {ip4addr["address"][1]}/{ip4addr["prefix"][1]}') - - for dns in ip4_conf.nameserver_data: - print("DNS: ", dns["address"][1]) - - if generic_device.device_type == DeviceType.WIFI: - wifidevice = NetworkDeviceWireless(device_path) - print("Wifi: ", WiFiOperationMode(wifidevice.mode).name.title()) - ap = AccessPoint(wifidevice.active_access_point) - if ap.ssid: - print("SSID: ", ap.ssid.decode("utf-8", "ignore")) - if ap.strength: - print("Signal: ", ap.strength) - - print("") - - -if __name__ == "__main__": - sdbus.set_default_bus(sdbus.sd_bus_open_system()) - list_networkdevice_details_blocking() 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/mypy.ini b/mypy.ini new file mode 100644 index 0000000..c19fe76 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +namespace_packages = true + diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4b5d8d0..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sdbus>=0.8rc2 diff --git a/sdbus_async/networkmanager/__init__.py b/sdbus_async/networkmanager/__init__.py index 6435006..81a390c 100644 --- a/sdbus_async/networkmanager/__init__.py +++ b/sdbus_async/networkmanager/__init__.py @@ -20,30 +20,43 @@ from __future__ import annotations from .enums import ( - AccessPointCapabilities, - BluetoothCapabilities, - ConnectionFlags, - ConnectionState, - ConnectionStateFlags, - ConnectionStateReason, + ActivationStateFlags, + ActiveConnectionState, + ActiveConnectionStateReason, + BluetoothCapabilitiesFlags, + CheckpointCreateFlags, + CheckpointRollbackResult, + ConnectionMultiConnect, ConnectionType, ConnectivityState, - DeviceCapabilities, + DeviceCapabilitiesFlags, DeviceInterfaceFlags, DeviceMetered, + DeviceReapplyFlags, DeviceState, DeviceStateReason, DeviceType, IpTunnelMode, - ModemCapabilities, - NetworkManagerConnectivityState, + ModemCapabilitiesFlags, + MptcpFlags, + NetworkManagerCapabilitiesFlags, + NetworkManagerReloadFlags, NetworkManagerState, - SecretAgentCapabilities, + RadioFlags, + SecretAgentCapabilitiesFlags, + SecretAgentGetSecretsFlags, + SettingsAddConnection2Flags, + SettingsConnectionFlags, + SettingsUpdate2Flags, + VpnConnectionState, + VpnConnectionStateReason, VpnFailure, - VpnState, + VpnServiceState, + WifiAccessPointCapabilitiesFlags, + WifiAccessPointSecurityFlags, + WifiCapabilitiesFlags, WiFiOperationMode, - WirelessCapabilities, - WpaSecurityFlags, + WimaxNSPNetworkType, ) from .exceptions import ( NetworkManagerAlreadyAsleepOrAwakeError, @@ -136,6 +149,7 @@ NetworkManagerDeviceWiredInterfaceAsync, NetworkManagerDeviceWireGuardInterfaceAsync, NetworkManagerDeviceWirelessInterfaceAsync, + NetworkManagerLoopbackInterfaceAsync, NetworkManagerPPPInterfaceAsync, ) from .interfaces_other import ( @@ -171,6 +185,7 @@ NetworkDeviceBridge, NetworkDeviceGeneric, NetworkDeviceIpTunnel, + NetworkDeviceLoopback, NetworkDeviceMacsec, NetworkDeviceMacvlan, NetworkDeviceModem, @@ -194,55 +209,6 @@ NetworkManagerSettings, WiFiP2PPeer, ) -from .settings.adsl import AdslSettings -from .settings.bluetooth import BluetoothSettings -from .settings.bond import BondSettings -from .settings.bond_port import BondPortSettings -from .settings.bridge import BridgeSettings -from .settings.bridge_port import BridgePortSettings -from .settings.cdma import CdmaSettings -from .settings.connection import ConnectionSettings -from .settings.datatypes import AddressData, RouteData, WireguardPeers -from .settings.dcb import DcbSettings -from .settings.ethernet import EthernetSettings -from .settings.gsm import GsmSettings -from .settings.hostname import HostnameSettings -from .settings.ieee802_1x import Ieee8021XSettings -from .settings.infiniband import InfinibandSettings -from .settings.ip_tunnel import IpTunnelSettings -from .settings.ipv4 import Ipv4Settings -from .settings.ipv6 import Ipv6Settings -from .settings.lowpan import LowpanSettings -from .settings.macsec import MacsecSettings -from .settings.macvlan import MacvlanSettings -from .settings.match import MatchSettings -from .settings.olpc_mesh import OlpcMeshSettings -from .settings.ovs_bridge import OvsBridgeSettings -from .settings.ovs_dpdk import OvsDpdkSettings -from .settings.ovs_external_ids import OvsExternalIdsSettings -from .settings.ovs_interface import OvsInterfaceSettings -from .settings.ovs_patch import OvsPatchSettings -from .settings.ovs_port import OvsPortSettings -from .settings.ppp import PppSettings -from .settings.pppoe import PppoeSettings -from .settings.profile import ConnectionProfile -from .settings.proxy import ProxySettings -from .settings.serial import SerialSettings -from .settings.team import TeamSettings -from .settings.team_port import TeamPortSettings -from .settings.tun import TunSettings -from .settings.user import UserSettings -from .settings.veth import VethSettings -from .settings.vlan import VlanSettings -from .settings.vpn import VpnSettings -from .settings.vrf import VrfSettings -from .settings.vxlan import VxlanSettings -from .settings.wifi_p2p import WifiP2PSettings -from .settings.wimax import WimaxSettings -from .settings.wireguard import WireguardSettings -from .settings.wireless import WirelessSettings -from .settings.wireless_security import WirelessSecuritySettings -from .settings.wpan import WpanSettings from .types import ( NetworkManagerConnectionProperties, NetworkManagerSetting, @@ -265,30 +231,43 @@ __all__ = ( # .enums - 'AccessPointCapabilities', - 'BluetoothCapabilities', - 'ConnectionFlags', - 'ConnectionState', - 'ConnectionStateFlags', - 'ConnectionStateReason', + 'ActivationStateFlags', + 'ActiveConnectionState', + 'ActiveConnectionStateReason', + 'BluetoothCapabilitiesFlags', + 'CheckpointCreateFlags', + 'CheckpointRollbackResult', + 'ConnectionMultiConnect', 'ConnectionType', 'ConnectivityState', - 'DeviceCapabilities', + 'DeviceCapabilitiesFlags', 'DeviceInterfaceFlags', 'DeviceMetered', + 'DeviceReapplyFlags', 'DeviceState', 'DeviceStateReason', 'DeviceType', 'IpTunnelMode', - 'ModemCapabilities', - 'NetworkManagerConnectivityState', + 'ModemCapabilitiesFlags', + 'MptcpFlags', + 'NetworkManagerCapabilitiesFlags', + 'NetworkManagerReloadFlags', 'NetworkManagerState', - 'SecretAgentCapabilities', + 'RadioFlags', + 'SecretAgentCapabilitiesFlags', + 'SecretAgentGetSecretsFlags', + 'SettingsAddConnection2Flags', + 'SettingsConnectionFlags', + 'SettingsUpdate2Flags', + 'VpnConnectionState', + 'VpnConnectionStateReason', 'VpnFailure', - 'VpnState', + 'VpnServiceState', + 'WifiAccessPointCapabilitiesFlags', + 'WifiAccessPointSecurityFlags', + 'WifiCapabilitiesFlags', 'WiFiOperationMode', - 'WirelessCapabilities', - 'WpaSecurityFlags', + 'WimaxNSPNetworkType', # .exceptions 'NetworkManagerAlreadyAsleepOrAwakeError', 'NetworkManagerAlreadyEnabledOrDisabledError', @@ -379,6 +358,7 @@ 'NetworkManagerDeviceWiredInterfaceAsync', 'NetworkManagerDeviceWireGuardInterfaceAsync', 'NetworkManagerDeviceWirelessInterfaceAsync', + 'NetworkManagerLoopbackInterfaceAsync', 'NetworkManagerPPPInterfaceAsync', # .interfaces_other 'NetworkManagerAccessPointInterfaceAsync', @@ -412,6 +392,7 @@ 'NetworkDeviceBridge', 'NetworkDeviceGeneric', 'NetworkDeviceIpTunnel', + 'NetworkDeviceLoopback', 'NetworkDeviceMacsec', 'NetworkDeviceMacvlan', 'NetworkDeviceModem', @@ -434,56 +415,6 @@ 'NetworkManagerDnsManager', 'NetworkManagerSettings', 'WiFiP2PPeer', - # .settings - 'AdslSettings', - 'BluetoothSettings', - 'BondSettings', - 'BondPortSettings', - 'BridgeSettings', - 'BridgePortSettings', - 'CdmaSettings', - 'ConnectionSettings', - 'AddressData', 'RouteData', 'WireguardPeers', - 'DcbSettings', - 'EthernetSettings', - 'GsmSettings', - 'HostnameSettings', - 'Ieee8021XSettings', - 'InfinibandSettings', - 'IpTunnelSettings', - 'Ipv4Settings', - 'Ipv6Settings', - 'LowpanSettings', - 'MacsecSettings', - 'MacvlanSettings', - 'MatchSettings', - 'OlpcMeshSettings', - 'OvsBridgeSettings', - 'OvsDpdkSettings', - 'OvsExternalIdsSettings', - 'OvsInterfaceSettings', - 'OvsPatchSettings', - 'OvsPortSettings', - 'PppSettings', - 'PppoeSettings', - 'ConnectionProfile', - 'ProxySettings', - 'SerialSettings', - 'TeamSettings', - 'TeamPortSettings', - 'TunSettings', - 'UserSettings', - 'VethSettings', - 'VlanSettings', - 'VpnSettings', - 'VrfSettings', - 'VxlanSettings', - 'WifiP2PSettings', - 'WimaxSettings', - 'WireguardSettings', - 'WirelessSettings', - 'WirelessSecuritySettings', - 'WpanSettings', # .types 'NetworkManagerConnectionProperties', 'NetworkManagerSetting', diff --git a/sdbus_async/networkmanager/enums.py b/sdbus_async/networkmanager/enums.py index 5a4ca10..beb1788 100644 --- a/sdbus_async/networkmanager/enums.py +++ b/sdbus_async/networkmanager/enums.py @@ -17,748 +17,1347 @@ # 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 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. - Flags: + The range 0x7000 - 0x7FFF of capabilities is guaranteed not to be + used by upstream NetworkManager. It could thus be used for + downstream extensions. - * NONE - * PRIVACY - * WPS - * WPS_BUTTON - * WPS_PIN + Since NetworkManager 1.6. """ - 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 + + 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. """ - 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 -class WiFiOperationMode(IntEnum): - """Operation mode of WiFi access point +class NetworkManagerState(IntEnum): + """Indicates the current overall networking state.""" - * UNKNOWN - * ADHOC - * INFRASTRUCTURE - * AP - * MESH - """ 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 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 interfac.e""" + WIREGUARD = 29 + """A WireGuard interface.""" + WIFI_P2P = 30 + """An 802.11 Wi-Fi P2P device. -class ConnectionStateFlags(IntFlag): - """State of connection flags + Since NetworkManager 1.16. + """ + VRF = 31 + """A VRF (Virtual Routing and Forwarding) interface. - Flags: + Since NetworkManager 1.24. + """ + LOOPBACK = 32 + """A loopback 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.42. """ - 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 -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 +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. + + Since NetworkManager 1.22. """ - 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 -class BluetoothCapabilities(IntFlag): - """Bluetooth Capabilities +class WifiAccessPointCapabilitiesFlags(IntFlag): + """802.11 access point flags.""" - Flags: + NONE = 0x00000000 + """Access point has no special capabilities.""" + PRIVACY = 0x00000001 + """Access point requires authentication and encryption. - * NONE - * DIAL_UP - * NETWORK_ACCESS_POINT + Usually means WEP. """ - NONE = 0x0 - DIAL_UP = 0x1 - NETWORK_ACCESS_POINT = 0x2 + 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 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 WifiAccessPointSecurityFlags(IntFlag): + """802.11 access point security and authentication flags. + + These flags describe the current security requirements of an access point + as determined from the access point's beacon. """ - 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 + 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. + """ + KEY_MGMT_EAP_SUITE_B_192 = 0x00002000 + """WPA3 Enterprise Suite-B 192 bit mode is supported. -class ModemCapabilities(IntFlag): - """Modem capabilities flags + Since NetworkManager 1.30. + """ - Flags: - * NONE - * ANALOG_WIRE - * CDMA - * GSM - * LTE +class WiFiOperationMode(IntEnum): + """Indicates the 802.11 mode an access point or device is currently in.""" + + 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. """ - 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 + AP = 3 + """The device is an access point/hotspot. + + Not valid for access point objects; used only for hotspot mode on the + local machine. """ - 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 + MESH = 4 + """The device is a 802.11s mesh point. + + Since NetworkManager 1.20. + """ + +class BluetoothCapabilitiesFlags(IntFlag): + """Bluetooth device capabilities.""" -class DeviceCapabilities(IntFlag): - """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.""" - Flags: - * NONE - * SUPPORTED - * CARRIER_DETECTABLE - * IS_SOFTWARE - * CAN_SRIOV +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 = 0x0 - SUPPORTED = 0x1 - CARRIER_DETECTABLE = 0x2 - IS_SOFTWARE = 0x4 - CAN_SRIOV = 0x8 + + 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 State - - * UNKNOWN - * UNMANAGED - * UNAVAILABLE - * DISCONNECTED - * PREPARE - * CONFIG - * NEED_AUTH - * IP_CONFIG - * IP_CHECK - * SECONDARIES - * ACTIVATED - * DEACTIVATING - * FAILED - """ + """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.""" -# 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/=/ = /' -# -# One src/core/nm-device-*.c can support more than one ConnectionType, -# thus there are more ConnectionTypes than DeviceTypes: +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. + """ -# From NetworkManager-1.35: -class ConnectionType(str, Enum): - """Connection Types + 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.""" - * ADSL - * BLUETOOTH - * BOND - * BRIDGE - * CDMA - * DUMMY - * GSM - * INFINIBAND - * IP_TUNNEL - * MACSEC - * MACVLAN - * OLPC_MESH - * OVS_BRIDGE - * OVS_INTERFACE - * OVS_INTERFACE - * PPPOE - * SIXLOWPAN - * TEAM - * TUN - * VETH - * VLAN - * VPN - * VRF - * VXLAN - * WIFI_P2P - * WIRED - * WIREGUARD - * WIFI - * WPAN + +class ConnectionMultiConnect(IntEnum): + """Ability of a connection to be active on multiple devices. + + Since NetworkManager 1.14. """ - ADSL = "adsl" - BLUETOOTH = "bluetooth" - BOND = "bond" - BRIDGE = "bridge" - CDMA = "cdma" - DUMMY = "dummy" - GSM = "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" - WIRED = "802-3-ethernet" - WIREGUARD = "wireguard" - WIFI = "802-11-wireless" - WPAN = "wpan" + DEFAULT = 0 + """Indicates that the per-connection setting is unspecified. -class DeviceType(IntEnum): - """Device Type + 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. - * 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 + Activating a profile that is already active, will first deactivate it. """ - 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 + 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 DeviceMetered(IntEnum): - """Device Metered state - * UNKNOWN - * YES - * NO - * GUESS_YES - * GUESS_NO - """ +class ActiveConnectionState(IntEnum): + """Indicates the state of an active connection to a specific network.""" + UNKNOWN = 0 - YES = 1 - NO = 2 - GUESS_YES = 3 - GUESS_NO = 4 + """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 ConnectivityState(IntEnum): - """Connectivity state +class ActiveConnectionStateReason(IntEnum): + """Active connection state reasons. - * UNKNOWN - * NONE - * PORTAL - * LIMITED - * FULL + 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.""" + + 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. - Flags: + 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. - * NONE - * UP - * LOWER_UP - * CARRIER + 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. + """ + + +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 RadioFlags(IntFlag): + """Flags related to radio interfaces. + + Since NetworkManager 1.38. """ - NONE = 0x0 - UNSAVED = 0x1 - GENERATED = 0x2 - VOLATILE = 0x4 - EXTERNAL = 0x8 + 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 VpnState(IntEnum): - """VPN State - * UNKNOWN - * INIT - * SHUTDOWN - * STARTING - * STARTED - * STOPPING - * STOPPED +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 - - * LOGIN_FAILURE - * CONNECT_FAILED - * BAD_IP_CONFIG - """ - LOGIN_FAILURE = 0 - CONNECT_FAILED = 1 - BAD_IP_CONFIG = 3 - +class VpnConnectionState(IntEnum): + """VPN connection states.""" -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.""" + +# 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. - * UNKNOWN - * ASLEEP - * DISCONNECTED - * DISCONNECTING - * CONNECTING - * CONNECTED_LOCAL - * CONNECTED_SITE - * GLOBAL + * 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/exceptions.py b/sdbus_async/networkmanager/exceptions.py index 146c531..7c7ab84 100644 --- a/sdbus_async/networkmanager/exceptions.py +++ b/sdbus_async/networkmanager/exceptions.py @@ -72,7 +72,7 @@ class NmAgentManagerUserCanceledError( ) -# Errors returned by Connetion objects. +# Errors returned by Connection objects. class NmConnectionFailedError( DbusFailedError, NetworkManagerBaseError): """Unknown or unspecified error.""" diff --git a/sdbus_async/networkmanager/interfaces_devices.py b/sdbus_async/networkmanager/interfaces_devices.py index e8d3924..e433e42 100644 --- a/sdbus_async/networkmanager/interfaces_devices.py +++ b/sdbus_async/networkmanager/interfaces_devices.py @@ -28,6 +28,8 @@ dbus_signal_async, ) +from .settings import ConnectionProfile + class NetworkManagerDeviceBluetoothInterfaceAsync( DbusInterfaceCommonAsync, @@ -43,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 @@ -90,7 +93,8 @@ class NetworkManagerDeviceIPTunnelInterfaceAsync( def mode(self) -> int: """Tunnel mode - See :py:class:`IpTunnelMode` + See :py:class:`IpTunnelMode + `. """ raise NotImplementedError @@ -303,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 @@ -705,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 @@ -728,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 @@ -848,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 @@ -856,7 +864,8 @@ def capabilities(self) -> int: def state(self) -> int: """Device state. - See :py:class:`DeviceState` + See :py:class:`DeviceState + `. """ raise NotImplementedError @@ -864,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 @@ -936,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 @@ -959,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 @@ -985,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 @@ -993,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 @@ -1001,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 @@ -1016,10 +1033,48 @@ 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 + async def get_applied_connection_profile( + self + ) -> Tuple[ConnectionProfile, int]: + """Get the currently applied connection on the device. + + .. note:: + + This method cannot fetch secrets. Use + :py:meth:`NetworkManagerSettingsConnectionInterfaceAsync.get_profile` + to acquire profile with secrets. + + :returns: Tuple of profile and version id. + :rtype: Tuple[ConnectionProfile, int] + """ + + connection_vardict, version_id = await self.get_applied_connection(0) + + return ConnectionProfile.from_dbus(connection_vardict), version_id + + async def reapply_profile( + self, + profile: ConnectionProfile, + version_id: int = 0, + ) -> None: + """Attempts to update the configuration of a device + without deactivating it. + + :param ConnectionProfile profile: Connection profile to update + with. + :param int version_id: If non-zero, the current version id of + the applied-connection must match. The current version id can be + retrieved via :py:meth:`get_applied_connection_profile`. + """ + await self.reapply(profile.to_dbus(), version_id, 0) + class NetworkManagerPPPInterfaceAsync( DbusInterfaceCommonAsync, @@ -1075,3 +1130,10 @@ async def set_ifindex( Not documented upstream. """ raise NotImplementedError + + +class NetworkManagerLoopbackInterfaceAsync( + DbusInterfaceCommonAsync, + interface_name='org.freedesktop.NetworkManager.Device.Loopback', +): + ... diff --git a/sdbus_async/networkmanager/interfaces_other.py b/sdbus_async/networkmanager/interfaces_other.py index b5992ab..2a38d67 100644 --- a/sdbus_async/networkmanager/interfaces_other.py +++ b/sdbus_async/networkmanager/interfaces_other.py @@ -28,6 +28,7 @@ dbus_signal_async, ) +from .settings import ConnectionProfile from .types import NetworkManagerConnectionProperties @@ -40,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 @@ -48,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 @@ -56,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 @@ -79,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 @@ -126,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 @@ -201,7 +207,8 @@ def devices(self) -> List[str]: def state(self) -> int: """Connection state - See :py:class:`ConnectionState` + See :py:class:`ActiveConnectionState + `. """ raise NotImplementedError @@ -209,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 @@ -269,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 @@ -283,7 +294,8 @@ class NetworkManagerVPNConnectionInterfaceAsync( def vpn_state(self) -> int: """VPN connection state - See :py:class:`ConnectionState` + See :py:class:`VpnConnectionState + `. """ raise NotImplementedError @@ -297,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 @@ -320,7 +335,7 @@ class NetworkManagerDHCP6ConfigInterfaceAsync( @dbus_property_async('a{sv}') def options(self) -> Dict[str, Tuple[str, Any]]: - """Options and configurations returned by DHCPv4 server""" + """Options and configurations returned by DHCPv6 server""" raise NotImplementedError @@ -594,7 +609,8 @@ def unsaved(self) -> bool: def flags(self) -> int: """Connection flags - See :py:class:`ConnectionFlags` + See :py:class:`SettingsConnectionFlags + `. """ raise NotImplementedError @@ -613,6 +629,53 @@ def removed(self) -> None: """Signal when connection is removed""" raise NotImplementedError + async def update_profile( + self, + profile: ConnectionProfile, + save_to_disk: bool = False, + ) -> None: + """Update connection using the profile dataclass. + + :param ConnectionProfile profile: Connection profile to update + with. + + :param bool save_to_disk: Make changes permanent by saving + updated values to disk. + + By default changes are temporary. (saved only to RAM) + """ + flags = 0 + + if save_to_disk: + flags |= 0x1 + else: + flags |= 0x2 + + await self.update2(profile.to_dbus(), flags, {}) + + async def get_profile(self, + fetch_secrets: bool = True) -> ConnectionProfile: + """Get the connection settings as the profile object. + + :param bool fetch_secrets: Retrieve secret values. (like VPN passwords) + Makes additional calls to NetworkManager. + """ + profile = ConnectionProfile.from_dbus(await self.get_settings()) + + if fetch_secrets: + secrets_name_generator = profile.update_secrets_generator() + try: + secrets_name = next(secrets_name_generator) + while True: + secret_profile = ConnectionProfile.from_dbus( + await self.get_secrets(secrets_name)) + + secrets_name = secrets_name_generator.send(secret_profile) + except StopIteration: + ... + + return profile + class NetworkManagerSettingsInterfaceAsync( DbusInterfaceCommonAsync, @@ -733,6 +796,36 @@ def connection_removed(self) -> str: """Signal when connection was removed with the path""" raise NotImplementedError + async def add_connection_profile( + self, + profile: ConnectionProfile, + save_to_disk: bool = False, + ) -> Tuple[str, None]: + """Add new connection using the profile object. + + :param ConnectionProfile profile: Connection profile to update + with. + + :param bool save_to_disk: Make changes permanent by saving + updated values to disk. + + By default changes are temporary. (saved only to RAM) + + :return: Object path of new connection and None + :rtype: Tuple[str, None] + """ + flags = 0 + + if save_to_disk: + flags |= 0x1 + else: + flags |= 0x2 + + return ( + (await self.add_connection2(profile.to_dbus(), flags, {}))[0], + None, + ) + class NetworkManagerVPNPluginInterfaceAsync( DbusInterfaceCommonAsync, @@ -831,7 +924,8 @@ async def new_secrets( def state(self) -> int: """VPN state - See :py:class:`VpnState` + See :py:class:`VpnServiceState + `. """ raise NotImplementedError @@ -839,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 @@ -872,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 @@ -891,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 @@ -946,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 @@ -1088,7 +1184,8 @@ async def check_connectivity( ) -> int: """Get current connectivity state - See :py:class:`NetworkManagerConnectivityState` + See :py:class:`ConnectivityState + `. """ raise NotImplementedError @@ -1101,7 +1198,8 @@ async def get_state( ) -> int: """Get current NetworkManager state - See :py:class:`NetworkManagerState` + See :py:class:`NetworkManagerState + `. """ raise NotImplementedError @@ -1212,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 @@ -1244,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 @@ -1252,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 @@ -1287,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 @@ -1300,3 +1402,33 @@ def device_added(self) -> str: def device_removed(self) -> str: """Signal when device had been removed with path""" raise NotImplementedError + + async def add_and_activate_connection_profile( + self, + profile: ConnectionProfile, + device_path: str = '/', + specific_object: str = '/', + ) -> Tuple[str, str]: + """Adds new connection using the profile object as template. + + :param ConnectionProfile profile: Connection profile to update + with. + + :param str device_path: Object path of device to be activated + using the given connection + + :param str specific_object: The path of a connection-type-specific + object this activation should use. + + This parameter is currently ignored for wired and mobile broadband + connections, and the value of "/" should be used. + + :return: Object path of new connection and path of active connection. + :rtype: Tuple[str, str] + """ + + return await self.add_and_activate_connection( + profile.to_dbus(), + device_path, + specific_object, + ) diff --git a/sdbus_async/networkmanager/objects.py b/sdbus_async/networkmanager/objects.py index 0c643fc..e03c69a 100644 --- a/sdbus_async/networkmanager/objects.py +++ b/sdbus_async/networkmanager/objects.py @@ -49,6 +49,7 @@ NetworkManagerDeviceWiredInterfaceAsync, NetworkManagerDeviceWireGuardInterfaceAsync, NetworkManagerDeviceWirelessInterfaceAsync, + NetworkManagerLoopbackInterfaceAsync, NetworkManagerPPPInterfaceAsync, ) from .interfaces_other import ( @@ -67,14 +68,13 @@ NetworkManagerVPNConnectionInterfaceAsync, NetworkManagerWifiP2PPeerInterfaceAsync, ) -from .settings.profile import ConnectionProfile from .types import NetworkManagerConnectionProperties NETWORK_MANAGER_SERVICE_NAME = 'org.freedesktop.NetworkManager' class NetworkManager(NetworkManagerInterfaceAsync): - """Network Manger main object + """Network Manager main object Implements :py:class:`NetworkManagerInterfaceAsync` @@ -181,12 +181,21 @@ async def get_connections_by_id(self, connection_id: str) -> List[str]: async def get_settings_by_uuid( self, connection_uuid: str ) -> NetworkManagerConnectionProperties: + """Helper to get a nested settings dict of a connection profile by uuid. + + :param str connection_uuid: The connection uuid of the connection profile + :return: Nested dictionary of all settings of the given connection profile + """ connection = await self.get_connection_by_uuid(connection_uuid) connection_manager = NetworkConnectionSettings(connection) connection_settings = await connection_manager.get_settings() return connection_settings async def delete_connection_by_uuid(self, connection_uuid: str) -> None: + """Helper to delete a connection profile identified by the connection uuid. + + :param str connection_uuid: The connection uuid of the connection profile + """ conn_dbus_path = await self.get_connection_by_uuid(connection_uuid) connection_settings_manager = NetworkConnectionSettings(conn_dbus_path) await connection_settings_manager.delete() @@ -215,9 +224,6 @@ def __init__(self, settings_path: str, settings_path, bus) - async def connection_profile(self) -> ConnectionProfile: - return ConnectionProfile.from_dbus(await self.get_settings()) - class NetworkDeviceGeneric( NetworkManagerDeviceInterfaceAsync, @@ -857,6 +863,36 @@ def __init__(self, device_path: str, bus: Optional[SdBus] = None) -> None: bus) +class NetworkDeviceLoopback( + NetworkManagerDeviceInterfaceAsync, + NetworkManagerDeviceStatisticsInterfaceAsync, + NetworkManagerLoopbackInterfaceAsync, +): + """Loopback device + + Implements :py:class:`NetworkManagerDeviceInterfaceAsync`, \ + :py:class:`NetworkManagerDeviceStatisticsInterfaceAsync` and \ + :py:class:`NetworkManagerLoopbackInterfaceAsync` + """ + + def __init__(self, device_path: str, bus: Optional[SdBus] = None) -> None: + """ + + :param device_path: D-Bus path to device object. \ + Obtained from \ + :py:meth:`NetworkManagerInterface.get_devices` or \ + :py:meth:`NetworkManagerInterface.get_device_by_ip_iface`. + + :param bus: You probably want to set default bus to system bus \ + or pass system bus directly. + """ + super().__init__() + self._connect( + NETWORK_MANAGER_SERVICE_NAME, + device_path, + bus) + + class ActiveConnection(NetworkManagerConnectionActiveInterfaceAsync): """Active connection object diff --git a/sdbus_async/networkmanager/settings/__init__.py b/sdbus_async/networkmanager/settings/__init__.py index 67eeb4d..60b03d0 100644 --- a/sdbus_async/networkmanager/settings/__init__.py +++ b/sdbus_async/networkmanager/settings/__init__.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. -from .connection import ConnectionSettings -from .ipv4 import Ipv4Settings -from .ipv6 import Ipv6Settings +from __future__ import annotations + +from .profile import ConnectionProfile from .adsl import AdslSettings from .bluetooth import BluetoothSettings from .bond import BondSettings @@ -11,13 +11,19 @@ from .bridge import BridgeSettings from .bridge_port import BridgePortSettings from .cdma import CdmaSettings +from .connection import ConnectionSettings from .dcb import DcbSettings +from .dummy import DummySettings +from .eapol import EapolSettings from .ethernet import EthernetSettings +from .ethtool import EthtoolSettings +from .generic import GenericSettings from .gsm import GsmSettings from .hostname import HostnameSettings -from .ieee802_1x import Ieee8021XSettings from .infiniband import InfinibandSettings from .ip_tunnel import IpTunnelSettings +from .ipv4 import Ipv4Settings +from .ipv6 import Ipv6Settings from .lowpan import LowpanSettings from .macsec import MacsecSettings from .macvlan import MacvlanSettings @@ -33,6 +39,8 @@ from .pppoe import PppoeSettings from .proxy import ProxySettings from .serial import SerialSettings +from .sriov import SriovSettings +from .tc import TcSettings from .team import TeamSettings from .team_port import TeamPortSettings from .tun import TunSettings @@ -49,10 +57,18 @@ from .wireless_security import WirelessSecuritySettings from .wpan import WpanSettings +from .datatypes import ( + AddressData, + RouteData, + LinkWatchers, + Vlans, + WireguardPeers, + RoutingRules, + Vfs, +) + __all__ = ( - 'ConnectionSettings', - 'Ipv4Settings', - 'Ipv6Settings', + 'ConnectionProfile', 'AdslSettings', 'BluetoothSettings', 'BondSettings', @@ -60,13 +76,19 @@ 'BridgeSettings', 'BridgePortSettings', 'CdmaSettings', + 'ConnectionSettings', 'DcbSettings', + 'DummySettings', + 'EapolSettings', 'EthernetSettings', + 'EthtoolSettings', + 'GenericSettings', 'GsmSettings', 'HostnameSettings', - 'Ieee8021XSettings', 'InfinibandSettings', 'IpTunnelSettings', + 'Ipv4Settings', + 'Ipv6Settings', 'LowpanSettings', 'MacsecSettings', 'MacvlanSettings', @@ -82,6 +104,8 @@ 'PppoeSettings', 'ProxySettings', 'SerialSettings', + 'SriovSettings', + 'TcSettings', 'TeamSettings', 'TeamPortSettings', 'TunSettings', @@ -97,4 +121,12 @@ 'WirelessSettings', 'WirelessSecuritySettings', 'WpanSettings', + + 'AddressData', + 'RouteData', + 'LinkWatchers', + 'Vlans', + 'WireguardPeers', + 'RoutingRules', + 'Vfs', ) diff --git a/sdbus_async/networkmanager/settings/adsl.py b/sdbus_async/networkmanager/settings/adsl.py index d4b4c11..5306e03 100644 --- a/sdbus_async/networkmanager/settings/adsl.py +++ b/sdbus_async/networkmanager/settings/adsl.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -10,32 +10,62 @@ @dataclass class AdslSettings(NetworkManagerSettingsMixin): """ADSL Settings""" + secret_fields_names = ['password'] + secret_name = 'adsl' encapsulation: Optional[str] = field( - metadata={'dbus_name': 'encapsulation', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'encapsulation', + 'dbus_type': 's', + }, default=None, ) + """Encapsulation of ADSL connection. Can be "vcmux" or "llc".""" password: Optional[str] = field( - metadata={'dbus_name': 'password', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'password', + 'dbus_type': 's', + }, default=None, ) + """Password used to authenticate with the ADSL service.""" password_flags: Optional[int] = field( - metadata={'dbus_name': 'password-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'password-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "password" property.""" protocol: Optional[str] = field( - metadata={'dbus_name': 'protocol', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'protocol', + 'dbus_type': 's', + }, default=None, ) + """ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm".""" username: Optional[str] = field( - metadata={'dbus_name': 'username', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'username', + 'dbus_type': 's', + }, default=None, ) + """Username used to authenticate with the ADSL service.""" vci: Optional[int] = field( - metadata={'dbus_name': 'vci', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'vci', + 'dbus_type': 'u', + }, default=None, ) + """VCI of ADSL connection""" vpi: Optional[int] = field( - metadata={'dbus_name': 'vpi', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'vpi', + 'dbus_type': 'u', + }, default=None, ) + """VPI of ADSL connection""" diff --git a/sdbus_async/networkmanager/settings/base.py b/sdbus_async/networkmanager/settings/base.py index 33ad163..c41b694 100644 --- a/sdbus_async/networkmanager/settings/base.py +++ b/sdbus_async/networkmanager/settings/base.py @@ -1,14 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later from __future__ import annotations -from dataclasses import fields + +from dataclasses import dataclass, fields from functools import lru_cache -from typing import Any, Dict, Type, cast +from typing import Any, ClassVar, Dict, List, Type, cast -from ..types import ( - NetworkManagerSettingsDomain, -) +from ..types import NetworkManagerSettingsDomain +@dataclass class NetworkManagerSettingsMixin: + secret_fields_names: ClassVar[List[str]] = [] + secret_name: ClassVar[str] = '' + def to_dbus(self) -> NetworkManagerSettingsDomain: """Return a dbus dictionary for NetworkManager to add/update profiles @@ -38,7 +42,7 @@ def to_settings_dict(self, defaults: bool = False) -> Dict[str, Any]: The key names provided are exactly as documented in these tables: https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html - Contrary to dataclasses.asdict(), it provides the orignal dbus keys, + Contrary to dataclasses.asdict(), it provides the original dbus keys, e.g. with numerical prefixes like "802-11-", dashes, and "id"/"type". In addition, it can be selected if defaults shall be omitted in output, @@ -79,10 +83,15 @@ def from_dbus( ) -> NetworkManagerSettingsMixin: """TODO: Add proper docstring""" reverse_mapping = cls.setting_name_reverse_mapping() - unvarianted_options = { - reverse_mapping[k]: cls._unpack_variant(k, *v) - for k, v in dbus_dict.items() - } + unvarianted_options = {} + for k, v in dbus_dict.items(): + try: + reverse_name = reverse_mapping[k] + except KeyError: + continue + + unvarianted_options[reverse_name] = cls._unpack_variant(k, *v) + return cls(**unvarianted_options) @classmethod diff --git a/sdbus_async/networkmanager/settings/bluetooth.py b/sdbus_async/networkmanager/settings/bluetooth.py index c678604..f5bb59d 100644 --- a/sdbus_async/networkmanager/settings/bluetooth.py +++ b/sdbus_async/networkmanager/settings/bluetooth.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,10 +12,19 @@ class BluetoothSettings(NetworkManagerSettingsMixin): """Bluetooth Settings""" bdaddr: Optional[bytes] = field( - metadata={'dbus_name': 'bdaddr', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'bdaddr', + 'dbus_type': 'ay', + }, default=None, ) + """The Bluetooth address of the device.""" bluetooth_type: Optional[str] = field( - metadata={'dbus_name': 'type', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'type', + 'dbus_type': 's', + }, default=None, ) + """Either "dun" for Dial-Up Networking connections or "panu" for Personal + Area Networking connections to devices supporting the NAP profile.""" diff --git a/sdbus_async/networkmanager/settings/bond.py b/sdbus_async/networkmanager/settings/bond.py index 701dba9..1eca116 100644 --- a/sdbus_async/networkmanager/settings/bond.py +++ b/sdbus_async/networkmanager/settings/bond.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -11,9 +11,23 @@ class BondSettings(NetworkManagerSettingsMixin): """Bonding Settings""" - interface_name: str = field( - metadata={'dbus_name': 'interface-name', 'dbus_type': 's'}, + interface_name: Optional[str] = field( + metadata={ + 'dbus_name': 'interface-name', + 'dbus_type': 's', + }, + default=None, ) - options: Dict[str, str] = field( - metadata={'dbus_name': 'options', 'dbus_type': 'a{ss}'}, + """Deprecated in favor of connection.interface-name, but can be used for + backward-compatibility with older daemons, to set the bond's + interface name.""" + options: Optional[Dict[str, str]] = field( + metadata={ + 'dbus_name': 'options', + 'dbus_type': 'a{ss}', + }, + default=None, ) + """Dictionary of key/value pairs of bonding options. Both keys and values + must be strings. Option names must contain only alphanumeric + characters (ie, [a-zA-Z0-9]).""" diff --git a/sdbus_async/networkmanager/settings/bond_port.py b/sdbus_async/networkmanager/settings/bond_port.py index 357cb3f..80d1806 100644 --- a/sdbus_async/networkmanager/settings/bond_port.py +++ b/sdbus_async/networkmanager/settings/bond_port.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,6 +12,11 @@ class BondPortSettings(NetworkManagerSettingsMixin): """Bond Port Settings""" queue_id: Optional[int] = field( - metadata={'dbus_name': 'queue-id', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'queue-id', + 'dbus_type': 'u', + }, default=None, ) + """The queue ID of this bond port. The maximum value of queue ID is the + number of TX queues currently active in device.""" diff --git a/sdbus_async/networkmanager/settings/bridge.py b/sdbus_async/networkmanager/settings/bridge.py index c1a7101..9cc8836 100644 --- a/sdbus_async/networkmanager/settings/bridge.py +++ b/sdbus_async/networkmanager/settings/bridge.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin from .datatypes import Vlans @@ -13,121 +13,280 @@ class BridgeSettings(NetworkManagerSettingsMixin): """Bridging Settings""" ageing_time: Optional[int] = field( - metadata={'dbus_name': 'ageing-time', 'dbus_type': 'u'}, - default=300, + metadata={ + 'dbus_name': 'ageing-time', + 'dbus_type': 'u', + }, + default=None, ) + """The Ethernet MAC address aging time, in seconds.""" forward_delay: Optional[int] = field( - metadata={'dbus_name': 'forward-delay', 'dbus_type': 'u'}, - default=15, + metadata={ + 'dbus_name': 'forward-delay', + 'dbus_type': 'u', + }, + default=None, ) + """The Spanning Tree Protocol (STP) forwarding delay, in seconds.""" group_address: Optional[bytes] = field( - metadata={'dbus_name': 'group-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'group-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, The MAC address of the multicast group this bridge uses + for STP. + + The address must be a link-local address in standard Ethernet MAC + address format, ie an address of the form 01:80:C2:00:00:0X, with X + in [0, 4..F]. If not specified the default value is + 01:80:C2:00:00:00.""" group_forward_mask: Optional[int] = field( - metadata={'dbus_name': 'group-forward-mask', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'group-forward-mask', + 'dbus_type': 'u', + }, default=None, ) + """A mask of group addresses to forward. Usually, group addresses in the + range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded + according to standards. This property is a mask of 16 bits, each + corresponding to a group address in that range that must be + forwarded. The mask can't have bits 0, 1 or 2 set because they are + used for STP, MAC pause frames and LACP.""" hello_time: Optional[int] = field( - metadata={'dbus_name': 'hello-time', 'dbus_type': 'u'}, - default=2, + metadata={ + 'dbus_name': 'hello-time', + 'dbus_type': 'u', + }, + default=None, ) + """The Spanning Tree Protocol (STP) hello time, in seconds.""" interface_name: Optional[str] = field( - metadata={'dbus_name': 'interface-name', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'interface-name', + 'dbus_type': 's', + }, default=None, ) + """Deprecated in favor of connection.interface-name, but can be used for + backward-compatibility with older daemons, to set the bridge's + interface name.""" mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, the MAC address of bridge. When creating a new bridge, + this MAC address will be set. + + If this field is left unspecified, the "ethernet.cloned-mac-address" + is referred instead to generate the initial MAC address. Note that + setting "ethernet.cloned-mac-address" anyway overwrites the MAC + address of the bridge later while activating the bridge.""" max_age: Optional[int] = field( - metadata={'dbus_name': 'max-age', 'dbus_type': 'u'}, - default=20, + metadata={ + 'dbus_name': 'max-age', + 'dbus_type': 'u', + }, + default=None, ) + """The Spanning Tree Protocol (STP) maximum message age, in seconds.""" multicast_hash_max: Optional[int] = field( - metadata={'dbus_name': 'multicast-hash-max', 'dbus_type': 'u'}, - default=4096, + metadata={ + 'dbus_name': 'multicast-hash-max', + 'dbus_type': 'u', + }, + default=None, ) + """Set maximum size of multicast hash table (value must be a power of 2).""" multicast_last_member_count: Optional[int] = field( - metadata={'dbus_name': 'multicast-last-member-count', 'dbus_type': 'u'}, - default=2, + metadata={ + 'dbus_name': 'multicast-last-member-count', + 'dbus_type': 'u', + }, + default=None, ) + """Set the number of queries the bridge will send before stopping + forwarding a multicast group after a "leave" message has been + received.""" multicast_last_member_interval: Optional[int] = field( - metadata={'dbus_name': 'multicast-last-member-interval', - 'dbus_type': 't'}, - default=100, + metadata={ + 'dbus_name': 'multicast-last-member-interval', + 'dbus_type': 't', + }, + default=None, ) + """Set interval (in deciseconds) between queries to find remaining members + of a group, after a "leave" message is received.""" multicast_membership_interval: Optional[int] = field( - metadata={'dbus_name': 'multicast-membership-interval', - 'dbus_type': 't'}, - default=26000, + metadata={ + 'dbus_name': 'multicast-membership-interval', + 'dbus_type': 't', + }, + default=None, ) + """Set delay (in deciseconds) after which the bridge will leave a group, if + no membership reports for this group are received.""" multicast_querier: Optional[bool] = field( - metadata={'dbus_name': 'multicast-querier', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'multicast-querier', + 'dbus_type': 'b', + }, + default=None, ) + """Enable or disable sending of multicast queries by the bridge. If not + specified the option is disabled.""" multicast_querier_interval: Optional[int] = field( - metadata={'dbus_name': 'multicast-querier-interval', 'dbus_type': 't'}, - default=25500, + metadata={ + 'dbus_name': 'multicast-querier-interval', + 'dbus_type': 't', + }, + default=None, ) + """If no queries are seen after this delay (in deciseconds) has passed, the + bridge will start to send its own queries.""" multicast_query_interval: Optional[int] = field( - metadata={'dbus_name': 'multicast-query-interval', 'dbus_type': 't'}, - default=12500, + metadata={ + 'dbus_name': 'multicast-query-interval', + 'dbus_type': 't', + }, + default=None, ) + """Interval (in deciseconds) between queries sent by the bridge after the + end of the startup phase.""" multicast_query_response_interval: Optional[int] = field( - metadata={'dbus_name': 'multicast-query-response-interval', - 'dbus_type': 't'}, - default=1000, + metadata={ + 'dbus_name': 'multicast-query-response-interval', + 'dbus_type': 't', + }, + default=None, ) + """Set the Max Response Time/Max Response Delay (in deciseconds) for + IGMP/MLD queries sent by the bridge.""" multicast_query_use_ifaddr: Optional[bool] = field( - metadata={'dbus_name': 'multicast-query-use-ifaddr', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'multicast-query-use-ifaddr', + 'dbus_type': 'b', + }, + default=None, ) + """If enabled the bridge's own IP address is used as the source address for + IGMP queries otherwise the default of 0.0.0.0 is used.""" multicast_router: Optional[str] = field( - metadata={'dbus_name': 'multicast-router', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'multicast-router', + 'dbus_type': 's', + }, default=None, ) + """Sets bridge's multicast router. Multicast-snooping must be enabled for + this option to work. + + Supported values are: 'auto', 'disabled', 'enabled' to which kernel + assigns the numbers 1, 0, and 2, respectively. If not specified the + default value is 'auto' (1).""" multicast_snooping: Optional[bool] = field( - metadata={'dbus_name': 'multicast-snooping', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'multicast-snooping', + 'dbus_type': 'b', + }, + default=None, ) + """Controls whether IGMP snooping is enabled for this bridge. Note that if + snooping was automatically disabled due to hash collisions, the + system may refuse to enable the feature until the collisions are + resolved.""" multicast_startup_query_count: Optional[int] = field( - metadata={'dbus_name': 'multicast-startup-query-count', - 'dbus_type': 'u'}, - default=2, + metadata={ + 'dbus_name': 'multicast-startup-query-count', + 'dbus_type': 'u', + }, + default=None, ) + """Set the number of IGMP queries to send during startup phase.""" multicast_startup_query_interval: Optional[int] = field( - metadata={'dbus_name': 'multicast-startup-query-interval', - 'dbus_type': 't'}, - default=3125, + metadata={ + 'dbus_name': 'multicast-startup-query-interval', + 'dbus_type': 't', + }, + default=None, ) + """Sets the time (in deciseconds) between queries sent out at startup to + determine membership information.""" priority: Optional[int] = field( - metadata={'dbus_name': 'priority', 'dbus_type': 'u'}, - default=32768, + metadata={ + 'dbus_name': 'priority', + 'dbus_type': 'u', + }, + default=None, ) + """Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower + values are "better"; the lowest priority bridge will be elected the + root bridge.""" stp: Optional[bool] = field( - metadata={'dbus_name': 'stp', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'stp', + 'dbus_type': 'b', + }, + default=None, ) + """Controls whether Spanning Tree Protocol (STP) is enabled for this + bridge.""" vlan_default_pvid: Optional[int] = field( - metadata={'dbus_name': 'vlan-default-pvid', 'dbus_type': 'u'}, - default=1, + metadata={ + 'dbus_name': 'vlan-default-pvid', + 'dbus_type': 'u', + }, + default=None, ) + """The default PVID for the ports of the bridge, that is the VLAN id + assigned to incoming untagged frames.""" vlan_filtering: Optional[bool] = field( - metadata={'dbus_name': 'vlan-filtering', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'vlan-filtering', + 'dbus_type': 'b', + }, + default=None, ) + """Control whether VLAN filtering is enabled on the bridge.""" vlan_protocol: Optional[str] = field( - metadata={'dbus_name': 'vlan-protocol', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'vlan-protocol', + 'dbus_type': 's', + }, default=None, ) + """If specified, the protocol used for VLAN filtering. + + Supported values are: '802.1Q', '802.1ad'. If not specified the + default value is '802.1Q'.""" vlan_stats_enabled: Optional[bool] = field( - metadata={'dbus_name': 'vlan-stats-enabled', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'vlan-stats-enabled', + 'dbus_type': 'b', + }, + default=None, ) + """Controls whether per-VLAN stats accounting is enabled.""" vlans: Optional[List[Vlans]] = field( - metadata={'dbus_name': 'vlans', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': Vlans}, + metadata={ + 'dbus_name': 'vlans', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': Vlans, + }, default=None, ) + """Array of bridge VLAN objects. In addition to the VLANs specified here, + the bridge will also have the default-pvid VLAN configured by the + bridge.vlan-default-pvid property. + + In nmcli the VLAN list can be specified with the following syntax: + + $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... + + where $vid is either a single id between 1 and 4094 or a range, + represented as a couple of ids separated by a dash.""" diff --git a/sdbus_async/networkmanager/settings/bridge_port.py b/sdbus_async/networkmanager/settings/bridge_port.py index 56b6e96..1708ec1 100644 --- a/sdbus_async/networkmanager/settings/bridge_port.py +++ b/sdbus_async/networkmanager/settings/bridge_port.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin from .datatypes import Vlans @@ -13,20 +13,46 @@ class BridgePortSettings(NetworkManagerSettingsMixin): """Bridge Port Settings""" hairpin_mode: Optional[bool] = field( - metadata={'dbus_name': 'hairpin-mode', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'hairpin-mode', + 'dbus_type': 'b', + }, + default=None, ) + """Enables or disables "hairpin mode" for the port, which allows frames to + be sent back out through the port the frame was received on.""" path_cost: Optional[int] = field( - metadata={'dbus_name': 'path-cost', 'dbus_type': 'u'}, - default=100, + metadata={ + 'dbus_name': 'path-cost', + 'dbus_type': 'u', + }, + default=None, ) + """The Spanning Tree Protocol (STP) port cost for destinations via this + port.""" priority: Optional[int] = field( - metadata={'dbus_name': 'priority', 'dbus_type': 'u'}, - default=32, + metadata={ + 'dbus_name': 'priority', + 'dbus_type': 'u', + }, + default=None, ) + """The Spanning Tree Protocol (STP) priority of this bridge port.""" vlans: Optional[List[Vlans]] = field( - metadata={'dbus_name': 'vlans', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': Vlans}, + metadata={ + 'dbus_name': 'vlans', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': Vlans, + }, default=None, ) + """Array of bridge VLAN objects. In addition to the VLANs specified here, + the port will also have the default-pvid VLAN configured on the + bridge by the bridge.vlan-default-pvid property. + + In nmcli the VLAN list can be specified with the following syntax: + + $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... + + where $vid is either a single id between 1 and 4094 or a range, + represented as a couple of ids separated by a dash.""" diff --git a/sdbus_async/networkmanager/settings/cdma.py b/sdbus_async/networkmanager/settings/cdma.py index 92156fa..70c1d28 100644 --- a/sdbus_async/networkmanager/settings/cdma.py +++ b/sdbus_async/networkmanager/settings/cdma.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -10,24 +10,53 @@ @dataclass class CdmaSettings(NetworkManagerSettingsMixin): """CDMA-based Mobile Broadband Settings""" + secret_fields_names = ['password'] + secret_name = 'cdma' mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple frames.""" number: Optional[str] = field( - metadata={'dbus_name': 'number', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'number', + 'dbus_type': 's', + }, default=None, ) + """The number to dial to establish the connection to the CDMA-based mobile + broadband network, if any. If not specified, the default number + (#777) is used when required.""" password: Optional[str] = field( - metadata={'dbus_name': 'password', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'password', + 'dbus_type': 's', + }, default=None, ) + """The password used to authenticate with the network, if required. Many + providers do not require a password, or accept any password. But if + a password is required, it is specified here.""" password_flags: Optional[int] = field( - metadata={'dbus_name': 'password-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'password-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "password" property.""" username: Optional[str] = field( - metadata={'dbus_name': 'username', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'username', + 'dbus_type': 's', + }, default=None, ) + """The username used to authenticate with the network, if required. Many + providers do not require a username, or accept any username. But if + a username is required, it is specified here.""" diff --git a/sdbus_async/networkmanager/settings/connection.py b/sdbus_async/networkmanager/settings/connection.py index 5d04617..47416bf 100644 --- a/sdbus_async/networkmanager/settings/connection.py +++ b/sdbus_async/networkmanager/settings/connection.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -11,104 +11,469 @@ class ConnectionSettings(NetworkManagerSettingsMixin): """General Connection Profile Settings""" - connection_id: str = field( - metadata={'dbus_name': 'id', 'dbus_type': 's'}, - ) - connection_type: str = field( - metadata={'dbus_name': 'type', 'dbus_type': 's'}, - ) - uuid: str = field( - metadata={'dbus_name': 'uuid', 'dbus_type': 's'}, - ) auth_retries: Optional[int] = field( - metadata={'dbus_name': 'auth-retries', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'auth-retries', + 'dbus_type': 'i', + }, default=None, ) + """The number of retries for the authentication. Zero means to try + indefinitely; -1 means to use a global default. If the global + default is not set, the authentication retries for 3 times before + failing the connection. + + Currently, this only applies to 802-1x authentication.""" autoconnect: Optional[bool] = field( - metadata={'dbus_name': 'autoconnect', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'autoconnect', + 'dbus_type': 'b', + }, + default=None, ) + """Whether or not the connection should be automatically connected by + NetworkManager when the resources for the connection are available. + TRUE to automatically activate the connection, FALSE to require + manual intervention to activate the connection. + + Autoconnect happens when the circumstances are suitable. That means + for example that the device is currently managed and not active. + Autoconnect thus never replaces or competes with an already active + profile. + + Note that autoconnect is not implemented for VPN profiles. See + "secondaries" as an alternative to automatically connect VPN + profiles. + + If multiple profiles are ready to autoconnect on the same device, + the one with the better "connection.autoconnect-priority" is chosen. + If the priorities are equal, then the most recently connected + profile is activated. If the profiles were not connected earlier or + their "connection.timestamp" is identical, the choice is undefined. + + Depending on "connection.multi-connect", a profile can (auto)connect + only once at a time or multiple times.""" autoconnect_priority: Optional[int] = field( - metadata={'dbus_name': 'autoconnect-priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'autoconnect-priority', + 'dbus_type': 'i', + }, default=None, ) + """The autoconnect priority in range -999 to 999. If the connection is set + to autoconnect, connections with higher priority will be preferred. + The higher number means higher priority. Defaults to 0. Note that + this property only matters if there are more than one candidate + profile to select for autoconnect. In case of equal priority, the + profile used most recently is chosen.""" autoconnect_retries: Optional[int] = field( - metadata={'dbus_name': 'autoconnect-retries', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'autoconnect-retries', + 'dbus_type': 'i', + }, default=None, ) + """The number of times a connection should be tried when autoactivating + before giving up. Zero means forever, -1 means the global default (4 + times if not overridden). Setting this to 1 means to try activation + only once before blocking autoconnect. Note that after a timeout, + NetworkManager will try to autoconnect again.""" autoconnect_slaves: Optional[int] = field( - metadata={'dbus_name': 'autoconnect-slaves', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'autoconnect-slaves', + 'dbus_type': 'i', + }, default=None, ) + """Whether or not slaves of this connection should be automatically brought + up when NetworkManager activates this connection. This only has a + real effect for master connections. The properties "autoconnect", + "autoconnect-priority" and "autoconnect-retries" are unrelated to + this setting. The permitted values are: 0: leave slave connections + untouched, 1: activate all the slave connections with this + connection, -1: default. If -1 (default) is set, global + connection.autoconnect-slaves is read to determine the real value. + If it is default as well, this fallbacks to 0.""" dns_over_tls: Optional[int] = field( - metadata={'dbus_name': 'dns-over-tls', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dns-over-tls', + 'dbus_type': 'i', + }, default=None, ) + """Whether DNSOverTls (dns-over-tls) is enabled for the connection. + DNSOverTls is a technology which uses TLS to encrypt dns traffic. + + The permitted values are: "yes" (2) use DNSOverTls and disabled + fallback, "opportunistic" (1) use DNSOverTls but allow fallback to + unencrypted resolution, "no" (0) don't ever use DNSOverTls. If + unspecified "default" depends on the plugin used. Systemd-resolved + uses global setting. + + This feature requires a plugin which supports DNSOverTls. Otherwise, + the setting has no effect. One such plugin is dns-systemd-resolved.""" gateway_ping_timeout: Optional[int] = field( - metadata={'dbus_name': 'gateway-ping-timeout', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'gateway-ping-timeout', + 'dbus_type': 'u', + }, + default=None, + ) + """If greater than zero, delay success of IP addressing until either the + timeout is reached, or an IP gateway replies to a ping.""" + connection_id: Optional[str] = field( + metadata={ + 'dbus_name': 'id', + 'dbus_type': 's', + }, default=None, ) + """A human readable unique identifier for the connection, like "Work Wi-Fi" + or "T-Mobile 3G".""" interface_name: Optional[str] = field( - metadata={'dbus_name': 'interface-name', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'interface-name', + 'dbus_type': 's', + }, default=None, ) + """The name of the network interface this connection is bound to. If not + set, then the connection can be attached to any interface of the + appropriate type (subject to restrictions imposed by other + settings). + + For software devices this specifies the name of the created device. + + For connection types where interface names cannot easily be made + persistent (e.g. mobile broadband or USB Ethernet), this property + should not be used. Setting this property restricts the interfaces a + connection can be used with, and if interface names change or are + reordered the connection may be applied to the wrong interface.""" lldp: Optional[int] = field( - metadata={'dbus_name': 'lldp', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'lldp', + 'dbus_type': 'i', + }, default=None, ) + """Whether LLDP is enabled for the connection.""" llmnr: Optional[int] = field( - metadata={'dbus_name': 'llmnr', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'llmnr', + 'dbus_type': 'i', + }, default=None, ) + """Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the + connection. LLMNR is a protocol based on the Domain Name System + (DNS) packet format that allows both IPv4 and IPv6 hosts to perform + name resolution for hosts on the same local link. + + The permitted values are: "yes" (2) register hostname and resolving + for the connection, "no" (0) disable LLMNR for the interface, + "resolve" (1) do not register hostname but allow resolving of LLMNR + host names If unspecified, "default" ultimately depends on the DNS + plugin (which for systemd-resolved currently means "yes"). + + This feature requires a plugin which supports LLMNR. Otherwise, the + setting has no effect. One such plugin is dns-systemd-resolved.""" master: Optional[str] = field( - metadata={'dbus_name': 'master', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'master', + 'dbus_type': 's', + }, default=None, ) + """Interface name of the master device or UUID of the master connection.""" mdns: Optional[int] = field( - metadata={'dbus_name': 'mdns', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'mdns', + 'dbus_type': 'i', + }, default=None, ) + """Whether mDNS is enabled for the connection. + + The permitted values are: "yes" (2) register hostname and resolving + for the connection, "no" (0) disable mDNS for the interface, + "resolve" (1) do not register hostname but allow resolving of mDNS + host names and "default" (-1) to allow lookup of a global default in + NetworkManager.conf. If unspecified, "default" ultimately depends on + the DNS plugin (which for systemd-resolved currently means "no"). + + This feature requires a plugin which supports mDNS. Otherwise, the + setting has no effect. One such plugin is dns-systemd-resolved.""" metered: Optional[int] = field( - metadata={'dbus_name': 'metered', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'metered', + 'dbus_type': 'i', + }, default=None, ) + """Whether the connection is metered. + + When updating this property on a currently activated connection, the + change takes effect immediately.""" + mptcp_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'mptcp-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Whether to configure MPTCP endpoints and the address flags. If MPTCP is + enabled in NetworkManager, it will configure the addresses of the + interface as MPTCP endpoints. Note that IPv4 loopback addresses + (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 + loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 + unique local addresses (ULA, fc00::/7) and IPv6 privacy extension + addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being + configured as endpoints. + + If "disabled" (0x1), MPTCP handling for the interface is disabled + and no endpoints are registered. + + The "enabled" (0x2) flag means that MPTCP handling is enabled. This + flag can also be implied from the presence of other flags. + + Even when enabled, MPTCP handling will by default still be disabled + unless "/proc/sys/net/mptcp/enabled" sysctl is on. NetworkManager + does not change the sysctl and this is up to the administrator or + distribution. To configure endpoints even if the sysctl is disabled, + "also-without-sysctl" (0x4) flag can be used. In that case, + NetworkManager doesn't look at the sysctl and configures endpoints + regardless. + + Even when enabled, NetworkManager will only configure MPTCP + endpoints for a certain address family, if there is a unicast + default route (0.0.0.0/0 or ::/0) in the main routing table. The + flag "also-without-default-route" (0x8) can override that. + + When MPTCP handling is enabled then endpoints are configured with + the specified address flags "signal" (0x10), "subflow" (0x20), + "backup" (0x40), "fullmesh" (0x80). See ip-mptcp(8) manual for + additional information about the flags. + + If the flags are zero (0x0), the global connection default from + NetworkManager.conf is honored. If still unspecified, the fallback + is "enabled,subflow". Note that this means that MPTCP is by default + done depending on the "/proc/sys/net/mptcp/enabled" sysctl. + + NetworkManager does not change the MPTCP limits nor enable MPTCP via + "/proc/sys/net/mptcp/enabled". That is a host configuration which + the admin can change via sysctl and ip-mptcp. + + Strict reverse path filtering (rp_filter) breaks many MPTCP use + cases, so when MPTCP handling for IPv4 addresses on the interface is + enabled, NetworkManager would loosen the strict reverse path + filtering (1) to the loose setting (2).""" mud_url: Optional[str] = field( - metadata={'dbus_name': 'mud-url', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'mud-url', + 'dbus_type': 's', + }, default=None, ) + """If configured, set to a Manufacturer Usage Description (MUD) URL that + points to manufacturer-recommended network policies for IoT devices. + It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a + valid URL starting with "https://". + + The special value "none" is allowed to indicate that no MUD URL is + used. + + If the per-profile value is unspecified (the default), a global + connection default gets consulted. If still unspecified, the + ultimate default is "none".""" multi_connect: Optional[int] = field( - metadata={'dbus_name': 'multi-connect', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'multi-connect', + 'dbus_type': 'i', + }, default=None, ) + """Specifies whether the profile can be active multiple times at a + particular moment. The value is of type NMConnectionMultiConnect.""" permissions: Optional[List[str]] = field( - metadata={'dbus_name': 'permissions', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'permissions', + 'dbus_type': 'as', + }, default=None, ) + """An array of strings defining what access a given user has to this + connection. If this is NULL or empty, all users are allowed to + access this connection; otherwise users are allowed if and only if + they are in this list. When this is not empty, the connection can + be active only when one of the specified users is logged into an + active session. Each entry is of the form "[type]:[id]:[reserved]"; + for example, "user:dcbw:blah". + + At this time only the "user" [type] is allowed. Any other values + are ignored and reserved for future use. [id] is the username that + this permission refers to, which may not contain the ":" character. + Any [reserved] information present must be ignored and is reserved + for future use. All of [type], [id], and [reserved] must be valid + UTF-8.""" read_only: Optional[bool] = field( - metadata={'dbus_name': 'read-only', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'read-only', + 'dbus_type': 'b', + }, + default=None, ) + """FALSE if the connection can be modified using the provided settings + service's D-Bus interface with the right privileges, or TRUE if the + connection is read-only and cannot be modified.""" secondaries: Optional[List[str]] = field( - metadata={'dbus_name': 'secondaries', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'secondaries', + 'dbus_type': 'as', + }, default=None, ) + """List of connection UUIDs that should be activated when the base + connection itself is activated. Currently, only VPN connections are + supported.""" slave_type: Optional[str] = field( - metadata={'dbus_name': 'slave-type', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'slave-type', + 'dbus_type': 's', + }, default=None, ) + """Setting name of the device type of this slave's master connection (eg, + "bond"), or NULL if this connection is not a slave.""" stable_id: Optional[str] = field( - metadata={'dbus_name': 'stable-id', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'stable-id', + 'dbus_type': 's', + }, default=None, ) + """This represents the identity of the connection used for various + purposes. It allows to configure multiple profiles to share the + identity. Also, the stable-id can contain placeholders that are + substituted dynamically and deterministically depending on the + context. + + The stable-id is used for generating IPv6 stable private addresses + with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the + generated cloned MAC address for ethernet.cloned-mac-address=stable + and wifi.cloned-mac-address=stable. It is also used as DHCP client + identifier with ipv4.dhcp-client-id=stable and to derive the DHCP + DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. + + Note that depending on the context where it is used, other + parameters are also seeded into the generation algorithm. For + example, a per-host key is commonly also included, so that different + systems end up generating different IDs. Or with ipv6.addr-gen- + mode=stable-privacy, also the device's name is included, so that + different interfaces yield different addresses. The per-host key is + the identity of your machine and stored in + /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual + about the secret-key and the host identity. + + The '$' character is treated special to perform dynamic + substitutions at runtime. Currently, supported are "${CONNECTION}", + "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively + create unique IDs per-connection, per-device, per-boot, or every + time. Note that "${DEVICE}" corresponds to the interface name of the + device and "${MAC}" is the permanent MAC address of the device. Any + unrecognized patterns following '$' are treated verbatim, however + are reserved for future use. You are thus advised to avoid '$' or + escape it as "$$". For example, set it to + "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this + connection that changes with every reboot and differs depending on + the interface where the profile activates. + + If the value is unset, a global connection default is consulted. If + the value is still unset, the default is similar to "${CONNECTION}" + and uses a unique, fixed ID for the connection.""" timestamp: Optional[int] = field( - metadata={'dbus_name': 'timestamp', 'dbus_type': 't'}, + metadata={ + 'dbus_name': 'timestamp', + 'dbus_type': 't', + }, + default=None, + ) + """The time, in seconds since the Unix Epoch, that the connection was last + _successfully_ fully activated. + + NetworkManager updates the connection timestamp periodically when + the connection is active to ensure that an active connection has the + latest timestamp. The property is only meant for reading (changes to + this property will not be preserved).""" + connection_type: Optional[str] = field( + metadata={ + 'dbus_name': 'type', + 'dbus_type': 's', + }, + default=None, + ) + """Base type of the connection. For hardware-dependent connections, should + contain the setting name of the hardware-type specific setting (ie, + "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for + non-hardware dependent connections like VPN or otherwise, should + contain the setting name of that setting type (ie, "vpn" or + "bridge", etc).""" + uuid: Optional[str] = field( + metadata={ + 'dbus_name': 'uuid', + 'dbus_type': 's', + }, default=None, ) + """A universally unique identifier for the connection, for example + generated with libuuid. It should be assigned when the connection + is created, and never changed as long as the connection still + applies to the same network. For example, it should not be changed + when the "id" property or NMSettingIP4Config changes, but might need + to be re-created when the Wi-Fi SSID, mobile broadband network + provider, or "type" property changes. + + The UUID must be in the format + "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only + hexadecimal characters and "-").""" + wait_activation_delay: Optional[int] = field( + metadata={ + 'dbus_name': 'wait-activation-delay', + 'dbus_type': 'i', + }, + default=None, + ) + """Time in milliseconds to wait for connection to be considered activated. + The wait will start after the pre-up dispatcher event. + + The value 0 means no wait time. The default value is -1, which + currently has the same meaning as no wait time.""" wait_device_timeout: Optional[int] = field( - metadata={'dbus_name': 'wait-device-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'wait-device-timeout', + 'dbus_type': 'i', + }, default=None, ) + """Timeout in milliseconds to wait for device at startup. During boot, + devices may take a while to be detected by the driver. This property + will cause to delay NetworkManager-wait-online.service and nm-online + to give the device a chance to appear. This works by waiting for the + given timeout until a compatible device for the profile is available + and managed. + + The value 0 means no wait time. The default value is -1, which + currently has the same meaning as no wait time.""" zone: Optional[str] = field( - metadata={'dbus_name': 'zone', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'zone', + 'dbus_type': 's', + }, default=None, ) + """The trust level of a the connection. Free form case-insensitive string + (for example "Home", "Work", "Public"). NULL or unspecified zone + means the connection will be placed in the default zone as defined + by the firewall. + + When updating this property on a currently activated connection, the + change takes effect immediately.""" diff --git a/sdbus_async/networkmanager/settings/datatypes.py b/sdbus_async/networkmanager/settings/datatypes.py index dc9a721..278012c 100644 --- a/sdbus_async/networkmanager/settings/datatypes.py +++ b/sdbus_async/networkmanager/settings/datatypes.py @@ -49,43 +49,43 @@ class LinkWatchers(NetworkManagerSettingsMixin): ) delay_up: Optional[int] = field( metadata={'dbus_delay-up': 'delay-up', 'dbus_type': 'u'}, - default=None + default=None, ) delay_down: Optional[int] = field( metadata={'dbus_name': 'delay-down', 'dbus_type': 'u'}, - default=None + default=None, ) init_wait: Optional[int] = field( metadata={'dbus_name': 'init-wait', 'dbus_type': 'u'}, - default=None + default=None, ) interval: Optional[int] = field( metadata={'dbus_name': 'interval', 'dbus_type': 'u'}, - default=None + default=None, ) missed_max: Optional[int] = field( metadata={'dbus_name': 'missed-max', 'dbus_type': 'u'}, - default=None + default=None, ) source_host: Optional[str] = field( metadata={'dbus_name': 'source-host', 'dbus_type': 's'}, - default=None + default=None, ) target_host: Optional[str] = field( metadata={'dbus_name': 'target-host', 'dbus_type': 's'}, - default=None + default=None, ) validate_active: Optional[bool] = field( metadata={'dbus_name': 'validate-active', 'dbus_type': 'b'}, - default=None + default=None, ) validate_inactive: Optional[bool] = field( metadata={'dbus_name': 'validate-inactive', 'dbus_type': 'b'}, - default=None + default=None, ) send_alway: Optional[bool] = field( metadata={'dbus_name': 'send-alway', 'dbus_type': 'b'}, - default=None + default=None, ) @@ -102,22 +102,160 @@ class Vlans(NetworkManagerSettingsMixin): vid_end: int = field( metadata={'dbus_name': 'vid-end', 'dbus_type': 'u'}, ) - pvid: bool = field( + pvid: Optional[bool] = field( metadata={'dbus_name': 'pvid', 'dbus_type': 'b'}, + default=None, ) - untagged: bool = field( + untagged: Optional[bool] = field( metadata={'dbus_name': 'untagged', 'dbus_type': 'b'}, + default=None ) @dataclass class WireguardPeers(NetworkManagerSettingsMixin): - public_key: str = field( + public_key: Optional[str] = field( metadata={'dbus_name': 'public-key', 'dbus_type': 's'}, + default=None, ) - endpoint: int = field( + endpoint: Optional[int] = field( metadata={'dbus_name': 'endpoint', 'dbus_type': 's'}, + default=None, ) - allowed_ips: List[str] = field( + allowed_ips: Optional[List[str]] = field( metadata={'dbus_name': 'allowed-ips', 'dbus_type': 'as'}, + default=None, + ) + + +@dataclass +class RoutingRules(NetworkManagerSettingsMixin): + action: Optional[int] = field( + metadata={'dbus_name': 'action', 'dbus_type': 'y'}, + default=None, + ) + dport_end: Optional[int] = field( + metadata={'dbus_name': 'dport-end', 'dbus_type': 'q'}, + default=None, + ) + dport_start: Optional[int] = field( + metadata={'dbus_name': 'dport-start', 'dbus_type': 'q'}, + default=None, + ) + family: Optional[int] = field( + metadata={'dbus_name': 'family', 'dbus_type': 'i'}, + default=None, + ) + from_prefix: Optional[str] = field( + metadata={'dbus_name': 'from', 'dbus_type': 's'}, + default=None, + ) + from_len: Optional[int] = field( + metadata={'dbus_name': 'from-len', 'dbus_type': 'y'}, + default=None, + ) + fwmark: Optional[int] = field( + metadata={'dbus_name': 'fwmark', 'dbus_type': 'u'}, + default=None, + ) + fwmask: Optional[int] = field( + metadata={'dbus_name': 'fwmask', 'dbus_type': 'u'}, + default=None, + ) + iifname: Optional[str] = field( + metadata={'dbus_name': 'iifname', 'dbus_type': 's'}, + default=None, + ) + invert: Optional[bool] = field( + metadata={'dbus_name': 'invert', 'dbus_type': 'b'}, + default=None, + ) + ipproto: Optional[str] = field( + metadata={'dbus_name': 'ipproto', 'dbus_type': 's'}, + default=None, + ) + oifname: Optional[str] = field( + metadata={'dbus_name': 'oifname', 'dbus_type': 's'}, + default=None, + ) + priority: Optional[int] = field( + metadata={'dbus_name': 'priority', 'dbus_type': 'u'}, + default=None, + ) + sport_end: Optional[int] = field( + metadata={'dbus_name': 'sport-end', 'dbus_type': 'q'}, + default=None, + ) + sport_start: Optional[int] = field( + metadata={'dbus_name': 'sport-start', 'dbus_type': 'q'}, + default=None, + ) + supress_prefixlength: Optional[int] = field( + metadata={'dbus_name': 'supress-prefixlength', 'dbus_type': 'i'}, + default=None, + ) + table: Optional[int] = field( + metadata={'dbus_name': 'table', 'dbus_type': 'u'}, + default=None, + ) + to: Optional[str] = field( + metadata={'dbus_name': 'to', 'dbus_type': 's'}, + default=None, + ) + tos: Optional[int] = field( + metadata={'dbus_name': 'tos', 'dbus_type': 'y'}, + default=None, + ) + to_len: Optional[int] = field( + metadata={'dbus_name': 'to-len', 'dbus_type': 'y'}, + default=None, ) + range_end: Optional[int] = field( + metadata={'dbus_name': 'range-end', 'dbus_type': 'u'}, + default=None, + ) + range_start: Optional[int] = field( + metadata={'dbus_name': 'range-start', 'dbus_type': 'u'}, + default=None, + ) + + +@dataclass +class Vfs(NetworkManagerSettingsMixin): + index: str = field( + metadata={'dbus_name': 'index', 'dbus_type': 's'}, + ) + mac: Optional[str] = field( + metadata={'dbus_name': 'mac', 'dbus_type': 's'}, + default=None, + ) + spoof_check: Optional[str] = field( + metadata={'dbus_name': 'spoof-check', 'dbus_type': 's'}, + default=None, + ) + trust: Optional[str] = field( + metadata={'dbus_name': 'trust', 'dbus_type': 's'}, + default=None, + ) + min_tx_rate: Optional[str] = field( + metadata={'dbus_name': 'min-tx-rate', 'dbus_type': 's'}, + default=None, + ) + max_tx_rate: Optional[str] = field( + metadata={'dbus_name': 'max-tx-rate', 'dbus_type': 's'}, + default=None, + ) + vlans: Optional[str] = field( + metadata={'dbus_name': 'vlans', 'dbus_type': 's'}, + default=None, + ) + + +@dataclass +class Qdiscs(NetworkManagerSettingsMixin): + ... + + +@dataclass +class Tfilters(NetworkManagerSettingsMixin): + ... diff --git a/sdbus_async/networkmanager/settings/dcb.py b/sdbus_async/networkmanager/settings/dcb.py index 95c4cc1..609d6a8 100644 --- a/sdbus_async/networkmanager/settings/dcb.py +++ b/sdbus_async/networkmanager/settings/dcb.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,62 +12,163 @@ class DcbSettings(NetworkManagerSettingsMixin): """Data Center Bridging Settings""" app_fcoe_flags: Optional[int] = field( - metadata={'dbus_name': 'app-fcoe-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'app-fcoe-flags', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may + be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), + NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING + (0x4).""" app_fcoe_mode: Optional[str] = field( - metadata={'dbus_name': 'app-fcoe-mode', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'app-fcoe-mode', + 'dbus_type': 's', + }, default=None, ) + """The FCoE controller mode; either "fabric" or "vn2vn". + + Since 1.34, NULL is the default and means "fabric". Before 1.34, + NULL was rejected as invalid and the default was "fabric".""" app_fcoe_priority: Optional[int] = field( - metadata={'dbus_name': 'app-fcoe-priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'app-fcoe-priority', + 'dbus_type': 'i', + }, default=None, ) + """The highest User Priority (0 - 7) which FCoE frames should use, or -1 + for default priority. Only used when the "app-fcoe-flags" property + includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag.""" app_fip_flags: Optional[int] = field( - metadata={'dbus_name': 'app-fip-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'app-fip-flags', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the NMSettingDcbFlags for the DCB FIP application. Flags may + be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), + NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING + (0x4).""" app_fip_priority: Optional[int] = field( - metadata={'dbus_name': 'app-fip-priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'app-fip-priority', + 'dbus_type': 'i', + }, default=None, ) + """The highest User Priority (0 - 7) which FIP frames should use, or -1 for + default priority. Only used when the "app-fip-flags" property + includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag.""" app_iscsi_flags: Optional[int] = field( - metadata={'dbus_name': 'app-iscsi-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'app-iscsi-flags', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the NMSettingDcbFlags for the DCB iSCSI application. Flags + may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), + NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING + (0x4).""" app_iscsi_priority: Optional[int] = field( - metadata={'dbus_name': 'app-iscsi-priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'app-iscsi-priority', + 'dbus_type': 'i', + }, default=None, ) + """The highest User Priority (0 - 7) which iSCSI frames should use, or -1 + for default priority. Only used when the "app-iscsi-flags" property + includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag.""" priority_bandwidth: Optional[List[int]] = field( - metadata={'dbus_name': 'priority-bandwidth', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'priority-bandwidth', + 'dbus_type': 'au', + }, default=None, ) + """An array of 8 uint values, where the array index corresponds to the User + Priority (0 - 7) and the value indicates the percentage of bandwidth + of the priority's assigned group that the priority may use. The sum + of all percentages for priorities which belong to the same group + must total 100 percents.""" priority_flow_control: Optional[List[int]] = field( - metadata={'dbus_name': 'priority-flow-control', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'priority-flow-control', + 'dbus_type': 'au', + }, default=None, ) + """An array of 8 boolean values, where the array index corresponds to the + User Priority (0 - 7) and the value indicates whether or not the + corresponding priority should transmit priority pause.""" priority_flow_control_flags: Optional[int] = field( - metadata={'dbus_name': 'priority-flow-control-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'priority-flow-control-flags', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). + Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), + NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING + (0x4).""" priority_group_bandwidth: Optional[List[int]] = field( - metadata={'dbus_name': 'priority-group-bandwidth', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'priority-group-bandwidth', + 'dbus_type': 'au', + }, default=None, ) + """An array of 8 uint values, where the array index corresponds to the + Priority Group ID (0 - 7) and the value indicates the percentage of + link bandwidth allocated to that group. Allowed values are 0 - 100, + and the sum of all values must total 100 percents.""" priority_group_flags: Optional[int] = field( - metadata={'dbus_name': 'priority-group-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'priority-group-flags', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the NMSettingDcbFlags for DCB Priority Groups. Flags may be + any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), + NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING + (0x4).""" priority_group_id: Optional[List[int]] = field( - metadata={'dbus_name': 'priority-group-id', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'priority-group-id', + 'dbus_type': 'au', + }, default=None, ) + """An array of 8 uint values, where the array index corresponds to the User + Priority (0 - 7) and the value indicates the Priority Group ID. + Allowed Priority Group ID values are 0 - 7 or 15 for the + unrestricted group.""" priority_strict_bandwidth: Optional[List[int]] = field( - metadata={'dbus_name': 'priority-strict-bandwidth', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'priority-strict-bandwidth', + 'dbus_type': 'au', + }, default=None, ) + """An array of 8 boolean values, where the array index corresponds to the + User Priority (0 - 7) and the value indicates whether or not the + priority may use all of the bandwidth allocated to its assigned + group.""" priority_traffic_class: Optional[List[int]] = field( - metadata={'dbus_name': 'priority-traffic-class', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'priority-traffic-class', + 'dbus_type': 'au', + }, default=None, ) + """An array of 8 uint values, where the array index corresponds to the User + Priority (0 - 7) and the value indicates the traffic class (0 - 7) + to which the priority is mapped.""" diff --git a/sdbus_async/networkmanager/settings/dummy.py b/sdbus_async/networkmanager/settings/dummy.py new file mode 100644 index 0000000..ef45504 --- /dev/null +++ b/sdbus_async/networkmanager/settings/dummy.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Optional +from .base import NetworkManagerSettingsMixin + + +@dataclass +class DummySettings(NetworkManagerSettingsMixin): + """Dummy Link Settings""" + diff --git a/sdbus_async/networkmanager/settings/eapol.py b/sdbus_async/networkmanager/settings/eapol.py new file mode 100644 index 0000000..74eb852 --- /dev/null +++ b/sdbus_async/networkmanager/settings/eapol.py @@ -0,0 +1,597 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import List, Optional +from .base import NetworkManagerSettingsMixin + + +@dataclass +class EapolSettings(NetworkManagerSettingsMixin): + """IEEE 802.1x Authentication Settings""" + secret_fields_names = ['ca_cert_password', 'client_cert_password', 'password', 'password_raw', 'phase2_ca_cert_password', 'phase2_client_cert_password', 'phase2_private_key_password', 'pin', 'private_key_password'] + secret_name = '802-1x' + + altsubject_matches: Optional[List[str]] = field( + metadata={ + 'dbus_name': 'altsubject-matches', + 'dbus_type': 'as', + }, + default=None, + ) + """List of strings to be matched against the altSubjectName of the + certificate presented by the authentication server. If the list is + empty, no verification of the server certificate's altSubjectName is + performed.""" + anonymous_identity: Optional[str] = field( + metadata={ + 'dbus_name': 'anonymous-identity', + 'dbus_type': 's', + }, + default=None, + ) + """Anonymous identity string for EAP authentication methods. Used as the + unencrypted identity with EAP types that support different tunneled + identity like EAP-TTLS.""" + auth_timeout: Optional[int] = field( + metadata={ + 'dbus_name': 'auth-timeout', + 'dbus_type': 'i', + }, + default=None, + ) + """A timeout for the authentication. Zero means the global default; if the + global default is not set, the authentication timeout is 25 seconds.""" + ca_cert: Optional[bytes] = field( + metadata={ + 'dbus_name': 'ca-cert', + 'dbus_type': 'ay', + }, + default=None, + ) + """Contains the CA certificate if used by the EAP method specified in the + "eap" property. + + Certificate data is specified using a "scheme"; three are currently + supported: blob, path and pkcs#11 URL. When using the blob scheme + this property should be set to the certificate's DER encoded data. + When using the path scheme, this property should be set to the full + UTF-8 encoded path of the certificate, prefixed with the string + "file://" and ending with a terminating NUL byte. This property can + be unset even if the EAP method supports CA certificates, but this + allows man-in-the-middle attacks and is NOT recommended. + + Note that enabling NMSetting8021x:system-ca-certs will override this + setting to use the built-in path, if the built-in path is not a + directory.""" + ca_cert_password: Optional[str] = field( + metadata={ + 'dbus_name': 'ca-cert-password', + 'dbus_type': 's', + }, + default=None, + ) + """The password used to access the CA certificate stored in "ca-cert" + property. Only makes sense if the certificate is stored on a PKCS#11 + token that requires a login.""" + ca_cert_password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'ca-cert-password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "ca-cert-password" property.""" + ca_path: Optional[str] = field( + metadata={ + 'dbus_name': 'ca-path', + 'dbus_type': 's', + }, + default=None, + ) + """UTF-8 encoded path to a directory containing PEM or DER formatted + certificates to be added to the verification chain in addition to + the certificate specified in the "ca-cert" property. + + If NMSetting8021x:system-ca-certs is enabled and the built-in CA + path is an existing directory, then this setting is ignored.""" + client_cert: Optional[bytes] = field( + metadata={ + 'dbus_name': 'client-cert', + 'dbus_type': 'ay', + }, + default=None, + ) + """Contains the client certificate if used by the EAP method specified in + the "eap" property. + + Certificate data is specified using a "scheme"; two are currently + supported: blob and path. When using the blob scheme (which is + backwards compatible with NM 0.7.x) this property should be set to + the certificate's DER encoded data. When using the path scheme, this + property should be set to the full UTF-8 encoded path of the + certificate, prefixed with the string "file://" and ending with a + terminating NUL byte.""" + client_cert_password: Optional[str] = field( + metadata={ + 'dbus_name': 'client-cert-password', + 'dbus_type': 's', + }, + default=None, + ) + """The password used to access the client certificate stored in "client- + cert" property. Only makes sense if the certificate is stored on a + PKCS#11 token that requires a login.""" + client_cert_password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'client-cert-password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "client-cert-password" property.""" + domain_match: Optional[str] = field( + metadata={ + 'dbus_name': 'domain-match', + 'dbus_type': 's', + }, + default=None, + ) + """Constraint for server domain name. If set, this list of FQDNs is used as + a match requirement for dNSName element(s) of the certificate + presented by the authentication server. If a matching dNSName is + found, this constraint is met. If no dNSName values are present, + this constraint is matched against SubjectName CN using the same + comparison. Multiple valid FQDNs can be passed as a ";" delimited + list.""" + domain_suffix_match: Optional[str] = field( + metadata={ + 'dbus_name': 'domain-suffix-match', + 'dbus_type': 's', + }, + default=None, + ) + """Constraint for server domain name. If set, this FQDN is used as a suffix + match requirement for dNSName element(s) of the certificate + presented by the authentication server. If a matching dNSName is + found, this constraint is met. If no dNSName values are present, + this constraint is matched against SubjectName CN using same suffix + match comparison. Since version 1.24, multiple valid FQDNs can be + passed as a ";" delimited list.""" + eap: Optional[List[str]] = field( + metadata={ + 'dbus_name': 'eap', + 'dbus_type': 'as', + }, + default=None, + ) + """The allowed EAP method to be used when authenticating to the network + with 802.1x. Valid methods are: "leap", "md5", "tls", "peap", + "ttls", "pwd", and "fast". Each method requires different + configuration using the properties of this setting; refer to + wpa_supplicant documentation for the allowed combinations.""" + identity: Optional[str] = field( + metadata={ + 'dbus_name': 'identity', + 'dbus_type': 's', + }, + default=None, + ) + """Identity string for EAP authentication methods. Often the user's user + or login name.""" + optional: Optional[bool] = field( + metadata={ + 'dbus_name': 'optional', + 'dbus_type': 'b', + }, + default=None, + ) + """Whether the 802.1X authentication is optional. If TRUE, the activation + will continue even after a timeout or an authentication failure. + Setting the property to TRUE is currently allowed only for Ethernet + connections. If set to FALSE, the activation can continue only after + a successful authentication.""" + pac_file: Optional[str] = field( + metadata={ + 'dbus_name': 'pac-file', + 'dbus_type': 's', + }, + default=None, + ) + """UTF-8 encoded file path containing PAC for EAP-FAST.""" + password: Optional[str] = field( + metadata={ + 'dbus_name': 'password', + 'dbus_type': 's', + }, + default=None, + ) + """UTF-8 encoded password used for EAP authentication methods. If both the + "password" property and the "password-raw" property are specified, + "password" is preferred.""" + password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "password" property.""" + password_raw: Optional[bytes] = field( + metadata={ + 'dbus_name': 'password-raw', + 'dbus_type': 'ay', + }, + default=None, + ) + """Password used for EAP authentication methods, given as a byte array to + allow passwords in other encodings than UTF-8 to be used. If both + the "password" property and the "password-raw" property are + specified, "password" is preferred.""" + password_raw_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'password-raw-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "password-raw" property.""" + phase1_auth_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'phase1-auth-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Specifies authentication flags to use in "phase 1" outer authentication + using NMSetting8021xAuthFlags options. The individual TLS versions + can be explicitly disabled. TLS time checks can be also disabled. If + a certain TLS disable flag is not set, it is up to the supplicant to + allow or forbid it. The TLS options map to tls_disable_tlsv1_x and + tls_disable_time_checks settings. See the wpa_supplicant + documentation for more details.""" + phase1_fast_provisioning: Optional[str] = field( + metadata={ + 'dbus_name': 'phase1-fast-provisioning', + 'dbus_type': 's', + }, + default=None, + ) + """Enables or disables in-line provisioning of EAP-FAST credentials when + FAST is specified as the EAP method in the "eap" property. + Recognized values are "0" (disabled), "1" (allow unauthenticated + provisioning), "2" (allow authenticated provisioning), and "3" + (allow both authenticated and unauthenticated provisioning). See + the wpa_supplicant documentation for more details.""" + phase1_peaplabel: Optional[str] = field( + metadata={ + 'dbus_name': 'phase1-peaplabel', + 'dbus_type': 's', + }, + default=None, + ) + """Forces use of the new PEAP label during key derivation. Some RADIUS + servers may require forcing the new PEAP label to interoperate with + PEAPv1. Set to "1" to force use of the new PEAP label. See the + wpa_supplicant documentation for more details.""" + phase1_peapver: Optional[str] = field( + metadata={ + 'dbus_name': 'phase1-peapver', + 'dbus_type': 's', + }, + default=None, + ) + """Forces which PEAP version is used when PEAP is set as the EAP method in + the "eap" property. When unset, the version reported by the server + will be used. Sometimes when using older RADIUS servers, it is + necessary to force the client to use a particular PEAP version. To + do so, this property may be set to "0" or "1" to force that specific + PEAP version.""" + phase2_altsubject_matches: Optional[List[str]] = field( + metadata={ + 'dbus_name': 'phase2-altsubject-matches', + 'dbus_type': 'as', + }, + default=None, + ) + """List of strings to be matched against the altSubjectName of the + certificate presented by the authentication server during the inner + "phase 2" authentication. If the list is empty, no verification of + the server certificate's altSubjectName is performed.""" + phase2_auth: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-auth', + 'dbus_type': 's', + }, + default=None, + ) + """Specifies the allowed "phase 2" inner authentication method when an EAP + method that uses an inner TLS tunnel is specified in the "eap" + property. For TTLS this property selects one of the supported non- + EAP inner methods: "pap", "chap", "mschap", "mschapv2" while + "phase2-autheap" selects an EAP inner method. For PEAP this selects + an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each + "phase 2" inner method requires specific parameters for successful + authentication; see the wpa_supplicant documentation for more + details. Both "phase2-auth" and "phase2-autheap" cannot be + specified.""" + phase2_autheap: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-autheap', + 'dbus_type': 's', + }, + default=None, + ) + """Specifies the allowed "phase 2" inner EAP-based authentication method + when TTLS is specified in the "eap" property. Recognized EAP-based + "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". + Each "phase 2" inner method requires specific parameters for + successful authentication; see the wpa_supplicant documentation for + more details.""" + phase2_ca_cert: Optional[bytes] = field( + metadata={ + 'dbus_name': 'phase2-ca-cert', + 'dbus_type': 'ay', + }, + default=None, + ) + """Contains the "phase 2" CA certificate if used by the EAP method + specified in the "phase2-auth" or "phase2-autheap" properties. + + Certificate data is specified using a "scheme"; three are currently + supported: blob, path and pkcs#11 URL. When using the blob scheme + this property should be set to the certificate's DER encoded data. + When using the path scheme, this property should be set to the full + UTF-8 encoded path of the certificate, prefixed with the string + "file://" and ending with a terminating NUL byte. This property can + be unset even if the EAP method supports CA certificates, but this + allows man-in-the-middle attacks and is NOT recommended. + + Note that enabling NMSetting8021x:system-ca-certs will override this + setting to use the built-in path, if the built-in path is not a + directory.""" + phase2_ca_cert_password: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-ca-cert-password', + 'dbus_type': 's', + }, + default=None, + ) + """The password used to access the "phase2" CA certificate stored in + "phase2-ca-cert" property. Only makes sense if the certificate is + stored on a PKCS#11 token that requires a login.""" + phase2_ca_cert_password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'phase2-ca-cert-password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "phase2-ca-cert-password" property.""" + phase2_ca_path: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-ca-path', + 'dbus_type': 's', + }, + default=None, + ) + """UTF-8 encoded path to a directory containing PEM or DER formatted + certificates to be added to the verification chain in addition to + the certificate specified in the "phase2-ca-cert" property. + + If NMSetting8021x:system-ca-certs is enabled and the built-in CA + path is an existing directory, then this setting is ignored.""" + phase2_client_cert: Optional[bytes] = field( + metadata={ + 'dbus_name': 'phase2-client-cert', + 'dbus_type': 'ay', + }, + default=None, + ) + """Contains the "phase 2" client certificate if used by the EAP method + specified in the "phase2-auth" or "phase2-autheap" properties. + + Certificate data is specified using a "scheme"; two are currently + supported: blob and path. When using the blob scheme (which is + backwards compatible with NM 0.7.x) this property should be set to + the certificate's DER encoded data. When using the path scheme, this + property should be set to the full UTF-8 encoded path of the + certificate, prefixed with the string "file://" and ending with a + terminating NUL byte. This property can be unset even if the EAP + method supports CA certificates, but this allows man-in-the-middle + attacks and is NOT recommended.""" + phase2_client_cert_password: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-client-cert-password', + 'dbus_type': 's', + }, + default=None, + ) + """The password used to access the "phase2" client certificate stored in + "phase2-client-cert" property. Only makes sense if the certificate + is stored on a PKCS#11 token that requires a login.""" + phase2_client_cert_password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'phase2-client-cert-password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "phase2-client-cert-password" + property.""" + phase2_domain_match: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-domain-match', + 'dbus_type': 's', + }, + default=None, + ) + """Constraint for server domain name. If set, this list of FQDNs is used as + a match requirement for dNSName element(s) of the certificate + presented by the authentication server during the inner "phase 2" + authentication. If a matching dNSName is found, this constraint is + met. If no dNSName values are present, this constraint is matched + against SubjectName CN using the same comparison. Multiple valid + FQDNs can be passed as a ";" delimited list.""" + phase2_domain_suffix_match: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-domain-suffix-match', + 'dbus_type': 's', + }, + default=None, + ) + """Constraint for server domain name. If set, this FQDN is used as a suffix + match requirement for dNSName element(s) of the certificate + presented by the authentication server during the inner "phase 2" + authentication. If a matching dNSName is found, this constraint is + met. If no dNSName values are present, this constraint is matched + against SubjectName CN using same suffix match comparison. Since + version 1.24, multiple valid FQDNs can be passed as a ";" delimited + list.""" + phase2_private_key: Optional[bytes] = field( + metadata={ + 'dbus_name': 'phase2-private-key', + 'dbus_type': 'ay', + }, + default=None, + ) + """Contains the "phase 2" inner private key when the "phase2-auth" or + "phase2-autheap" property is set to "tls". + + Key data is specified using a "scheme"; two are currently supported: + blob and path. When using the blob scheme and private keys, this + property should be set to the key's encrypted PEM encoded data. When + using private keys with the path scheme, this property should be set + to the full UTF-8 encoded path of the key, prefixed with the string + "file://" and ending with a terminating NUL byte. When using PKCS#12 + format private keys and the blob scheme, this property should be set + to the PKCS#12 data and the "phase2-private-key-password" property + must be set to password used to decrypt the PKCS#12 certificate and + key. When using PKCS#12 files and the path scheme, this property + should be set to the full UTF-8 encoded path of the key, prefixed + with the string "file://" and ending with a terminating NUL byte, + and as with the blob scheme the "phase2-private-key-password" + property must be set to the password used to decode the PKCS#12 + private key and certificate.""" + phase2_private_key_password: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-private-key-password', + 'dbus_type': 's', + }, + default=None, + ) + """The password used to decrypt the "phase 2" private key specified in the + "phase2-private-key" property when the private key either uses the + path scheme, or is a PKCS#12 format key.""" + phase2_private_key_password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'phase2-private-key-password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "phase2-private-key-password" + property.""" + phase2_subject_match: Optional[str] = field( + metadata={ + 'dbus_name': 'phase2-subject-match', + 'dbus_type': 's', + }, + default=None, + ) + """Substring to be matched against the subject of the certificate presented + by the authentication server during the inner "phase 2" + authentication. When unset, no verification of the authentication + server certificate's subject is performed. This property provides + little security, if any, and should not be used.""" + pin: Optional[str] = field( + metadata={ + 'dbus_name': 'pin', + 'dbus_type': 's', + }, + default=None, + ) + """PIN used for EAP authentication methods.""" + pin_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'pin-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "pin" property.""" + private_key: Optional[bytes] = field( + metadata={ + 'dbus_name': 'private-key', + 'dbus_type': 'ay', + }, + default=None, + ) + """Contains the private key when the "eap" property is set to "tls". + + Key data is specified using a "scheme"; two are currently supported: + blob and path. When using the blob scheme and private keys, this + property should be set to the key's encrypted PEM encoded data. When + using private keys with the path scheme, this property should be set + to the full UTF-8 encoded path of the key, prefixed with the string + "file://" and ending with a terminating NUL byte. When using PKCS#12 + format private keys and the blob scheme, this property should be set + to the PKCS#12 data and the "private-key-password" property must be + set to password used to decrypt the PKCS#12 certificate and key. + When using PKCS#12 files and the path scheme, this property should + be set to the full UTF-8 encoded path of the key, prefixed with the + string "file://" and ending with a terminating NUL byte, and as with + the blob scheme the "private-key-password" property must be set to + the password used to decode the PKCS#12 private key and certificate. + + WARNING: "private-key" is not a "secret" property, and thus + unencrypted private key data using the BLOB scheme may be readable + by unprivileged users. Private keys should always be encrypted with + a private key password to prevent unauthorized access to unencrypted + private key data.""" + private_key_password: Optional[str] = field( + metadata={ + 'dbus_name': 'private-key-password', + 'dbus_type': 's', + }, + default=None, + ) + """The password used to decrypt the private key specified in the "private- + key" property when the private key either uses the path scheme, or + if the private key is a PKCS#12 format key.""" + private_key_password_flags: Optional[int] = field( + metadata={ + 'dbus_name': 'private-key-password-flags', + 'dbus_type': 'u', + }, + default=None, + ) + """Flags indicating how to handle the "private-key-password" property.""" + subject_match: Optional[str] = field( + metadata={ + 'dbus_name': 'subject-match', + 'dbus_type': 's', + }, + default=None, + ) + """Substring to be matched against the subject of the certificate presented + by the authentication server. When unset, no verification of the + authentication server certificate's subject is performed. This + property provides little security, if any, and should not be used.""" + system_ca_certs: Optional[bool] = field( + metadata={ + 'dbus_name': 'system-ca-certs', + 'dbus_type': 'b', + }, + default=None, + ) + """When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using + the system CA directory specified at configure time with the + --system-ca-path switch. The certificates in this directory are + added to the verification chain in addition to any certificates + specified by the "ca-cert" and "phase2-ca-cert" properties. If the + path provided with --system-ca-path is rather a file name (bundle of + trusted CA certificates), it overrides "ca-cert" and "phase2-ca- + cert" properties instead (sets ca_cert/ca_cert2 options for + wpa_supplicant).""" diff --git a/sdbus_async/networkmanager/settings/ethernet.py b/sdbus_async/networkmanager/settings/ethernet.py index 07901b9..24f15d9 100644 --- a/sdbus_async/networkmanager/settings/ethernet.py +++ b/sdbus_async/networkmanager/settings/ethernet.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,66 +12,252 @@ class EthernetSettings(NetworkManagerSettingsMixin): """Wired Ethernet Settings""" accept_all_mac_addresses: Optional[int] = field( - metadata={'dbus_name': 'accept-all-mac-addresses', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'accept-all-mac-addresses', + 'dbus_type': 'i', + }, default=None, ) + """When TRUE, setup the interface to accept packets for all MAC addresses. + This is enabling the kernel interface flag IFF_PROMISC. When FALSE, + the interface will only accept the packets with the interface + destination mac address or broadcast.""" assigned_mac_address: Optional[str] = field( - metadata={'dbus_name': 'assigned-mac-address', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'assigned-mac-address', + 'dbus_type': 's', + }, default=None, ) + """The new field for the cloned MAC address. It can be either a hardware + address in ASCII representation, or one of the special values + "preserve", "permanent", "random" or "stable". This field replaces + the deprecated "cloned-mac-address" on D-Bus, which can only contain + explicit hardware addresses. Note that this property only exists in + D-Bus API. libnm and nmcli continue to call this property "cloned- + mac-address".""" auto_negotiate: Optional[bool] = field( - metadata={'dbus_name': 'auto-negotiate', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'auto-negotiate', + 'dbus_type': 'b', + }, + default=None, ) + """When TRUE, enforce auto-negotiation of speed and duplex mode. If "speed" + and "duplex" properties are both specified, only that single mode + will be advertised and accepted during the link auto-negotiation + process: this works only for BASE-T 802.3 specifications and is + useful for enforcing gigabits modes, as in these cases link + negotiation is mandatory. When FALSE, "speed" and "duplex" + properties should be both set or link configuration will be skipped.""" cloned_mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'cloned-mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'cloned-mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, request that the device use this MAC address instead. This + is known as MAC cloning or spoofing. + + Beside explicitly specifying a MAC address, the special values + "preserve", "permanent", "random" and "stable" are supported. + "preserve" means not to touch the MAC address on activation. + "permanent" means to use the permanent hardware address if the + device has one (otherwise this is treated as "preserve"). "random" + creates a random MAC address on each connect. "stable" creates a + hashed MAC address based on connection.stable-id and a machine + dependent key. + + If unspecified, the value can be overwritten via global defaults, + see manual of NetworkManager.conf. If still unspecified, it defaults + to "preserve" (older versions of NetworkManager may use a different + default value). + + On D-Bus, this field is expressed as "assigned-mac-address" or the + deprecated "cloned-mac-address".""" duplex: Optional[str] = field( - metadata={'dbus_name': 'duplex', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'duplex', + 'dbus_type': 's', + }, default=None, ) + """When a value is set, either "half" or "full", configures the device to + use the specified duplex mode. If "auto-negotiate" is "yes" the + specified duplex mode will be the only one advertised during link + negotiation: this works only for BASE-T 802.3 specifications and is + useful for enforcing gigabits modes, as in these cases link + negotiation is mandatory. If the value is unset (the default), the + link configuration will be either skipped (if "auto-negotiate" is + "no", the default) or will be auto-negotiated (if "auto-negotiate" + is "yes") and the local device will advertise all the supported + duplex modes. Must be set together with the "speed" property if + specified. Before specifying a duplex mode be sure your device + supports it.""" generate_mac_address_mask: Optional[str] = field( - metadata={'dbus_name': 'generate-mac-address-mask', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'generate-mac-address-mask', + 'dbus_type': 's', + }, default=None, ) + """With "cloned-mac-address" setting "random" or "stable", by default all + bits of the MAC address are scrambled and a locally-administered, + unicast MAC address is created. This property allows to specify that + certain bits are fixed. Note that the least significant bit of the + first MAC address will always be unset to create a unicast MAC + address. + + If the property is NULL, it is eligible to be overwritten by a + default connection setting. If the value is still NULL or an empty + string, the default is to create a locally-administered, unicast MAC + address. + + If the value contains one MAC address, this address is used as mask. + The set bits of the mask are to be filled with the current MAC + address of the device, while the unset bits are subject to + randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI + of the current MAC address and only randomize the lower 3 bytes + using the "random" or "stable" algorithm. + + If the value contains one additional MAC address after the mask, + this address is used instead of the current MAC address to fill the + bits that shall not be randomized. For example, a value of + "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC + address to 68:F7:28, while the lower bits are randomized. A value of + "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + globally-administered, burned-in MAC address. + + If the value contains more than one additional MAC addresses, one of + them is chosen randomly. For example, "02:00:00:00:00:00 + 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled + MAC address, randomly locally or globally administered.""" mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, this connection will only apply to the Ethernet device + whose permanent MAC address matches. This property does not change + the MAC address of the device (i.e. MAC spoofing).""" mac_address_blacklist: Optional[List[str]] = field( - metadata={'dbus_name': 'mac-address-blacklist', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'mac-address-blacklist', + 'dbus_type': 'as', + }, default=None, ) + """If specified, this connection will never apply to the Ethernet device + whose permanent MAC address matches an address in the list. Each + MAC address is in the standard hex-digits-and-colons notation + (00:11:22:33:44:55).""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple Ethernet frames.""" port: Optional[str] = field( - metadata={'dbus_name': 'port', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'port', + 'dbus_type': 's', + }, default=None, ) + """Specific port type to use if the device supports multiple attachment + methods. One of "tp" (Twisted Pair), "aui" (Attachment Unit + Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent + Interface). If the device supports only one port type, this setting + is ignored.""" s390_nettype: Optional[str] = field( - metadata={'dbus_name': 's390-nettype', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 's390-nettype', + 'dbus_type': 's', + }, default=None, ) + """s390 network device type; one of "qeth", "lcs", or "ctc", representing + the different types of virtual network devices available on s390 + systems.""" s390_options: Optional[Dict[str, str]] = field( - metadata={'dbus_name': 's390-options', 'dbus_type': 'a{ss}'}, + metadata={ + 'dbus_name': 's390-options', + 'dbus_type': 'a{ss}', + }, default=None, ) + """Dictionary of key/value pairs of s390-specific device options. Both + keys and values must be strings. Allowed keys include "portno", + "layer2", "portname", "protocol", among others. Key names must + contain only alphanumeric characters (ie, [a-zA-Z0-9]). + + Currently, NetworkManager itself does nothing with this information. + However, s390utils ships a udev rule which parses this information + and applies it to the interface.""" s390_subchannels: Optional[List[str]] = field( - metadata={'dbus_name': 's390-subchannels', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 's390-subchannels', + 'dbus_type': 'as', + }, default=None, ) + """Identifies specific subchannels that this network device uses for + communication with z/VM or s390 host. Like the "mac-address" + property for non-z/VM devices, this property can be used to ensure + this connection only applies to the network device that uses these + subchannels. The list should contain exactly 3 strings, and each + string may only be composed of hexadecimal characters and the period + (.) character.""" speed: Optional[int] = field( - metadata={'dbus_name': 'speed', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'speed', + 'dbus_type': 'u', + }, default=None, ) + """When a value greater than 0 is set, configures the device to use the + specified speed. If "auto-negotiate" is "yes" the specified speed + will be the only one advertised during link negotiation: this works + only for BASE-T 802.3 specifications and is useful for enforcing + gigabit speeds, as in this case link negotiation is mandatory. If + the value is unset (0, the default), the link configuration will be + either skipped (if "auto-negotiate" is "no", the default) or will be + auto-negotiated (if "auto-negotiate" is "yes") and the local device + will advertise all the supported speeds. In Mbit/s, ie 100 == + 100Mbit/s. Must be set together with the "duplex" property when non- + zero. Before specifying a speed value be sure your device supports + it.""" wake_on_lan: Optional[int] = field( - metadata={'dbus_name': 'wake-on-lan', 'dbus_type': 'u'}, - default=1, + metadata={ + 'dbus_name': 'wake-on-lan', + 'dbus_type': 'u', + }, + default=None, ) + """The NMSettingWiredWakeOnLan options to enable. Not all devices support + all options. May be any combination of + NM_SETTING_WIRED_WAKE_ON_LAN_PHY (0x2), + NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST (0x4), + NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST (0x8), + NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST (0x10), + NM_SETTING_WIRED_WAKE_ON_LAN_ARP (0x20), + NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC (0x40) or the special values + NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT (0x1) (to use global settings) + and NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE (0x8000) (to disable + management of Wake-on-LAN in NetworkManager).""" wake_on_lan_password: Optional[str] = field( - metadata={'dbus_name': 'wake-on-lan-password', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'wake-on-lan-password', + 'dbus_type': 's', + }, default=None, ) + """If specified, the password used with magic-packet-based Wake-on-LAN, + represented as an Ethernet MAC address. If NULL, no password will + be required.""" diff --git a/sdbus_async/networkmanager/settings/ethtool.py b/sdbus_async/networkmanager/settings/ethtool.py new file mode 100644 index 0000000..739e67d --- /dev/null +++ b/sdbus_async/networkmanager/settings/ethtool.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Optional +from .base import NetworkManagerSettingsMixin + + +@dataclass +class EthtoolSettings(NetworkManagerSettingsMixin): + """Ethtool Ethernet Settings""" + diff --git a/sdbus_async/networkmanager/settings/generic.py b/sdbus_async/networkmanager/settings/generic.py new file mode 100644 index 0000000..b633cd5 --- /dev/null +++ b/sdbus_async/networkmanager/settings/generic.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Optional +from .base import NetworkManagerSettingsMixin + + +@dataclass +class GenericSettings(NetworkManagerSettingsMixin): + """Generic Link Settings""" + diff --git a/sdbus_async/networkmanager/settings/gsm.py b/sdbus_async/networkmanager/settings/gsm.py index 13b96d2..4926d3a 100644 --- a/sdbus_async/networkmanager/settings/gsm.py +++ b/sdbus_async/networkmanager/settings/gsm.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -10,60 +10,148 @@ @dataclass class GsmSettings(NetworkManagerSettingsMixin): """GSM-based Mobile Broadband Settings""" + secret_fields_names = ['password', 'pin'] + secret_name = 'gsm' apn: Optional[str] = field( - metadata={'dbus_name': 'apn', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'apn', + 'dbus_type': 's', + }, default=None, ) + """The GPRS Access Point Name specifying the APN used when establishing a + data session with the GSM-based network. The APN often determines + how the user will be billed for their network usage and whether the + user has access to the Internet or just a provider-specific walled- + garden, so it is important to use the correct APN for the user's + mobile broadband plan. The APN may only be composed of the + characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9.""" auto_config: Optional[bool] = field( - metadata={'dbus_name': 'auto-config', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'auto-config', + 'dbus_type': 'b', + }, + default=None, ) + """When TRUE, the settings such as APN, username, or password will default + to values that match the network the modem will register to in the + Mobile Broadband Provider database.""" device_id: Optional[str] = field( - metadata={'dbus_name': 'device-id', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'device-id', + 'dbus_type': 's', + }, default=None, ) + """The device unique identifier (as given by the WWAN management service) + which this connection applies to. If given, the connection will + only apply to the specified device.""" home_only: Optional[bool] = field( - metadata={'dbus_name': 'home-only', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'home-only', + 'dbus_type': 'b', + }, + default=None, ) + """When TRUE, only connections to the home network will be allowed. + Connections to roaming networks will not be made.""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple frames.""" network_id: Optional[str] = field( - metadata={'dbus_name': 'network-id', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'network-id', + 'dbus_type': 's', + }, default=None, ) + """The Network ID (GSM LAI format, ie MCC-MNC) to force specific network + registration. If the Network ID is specified, NetworkManager will + attempt to force the device to register only on the specified + network. This can be used to ensure that the device does not roam + when direct roaming control of the device is not otherwise possible.""" number: Optional[str] = field( - metadata={'dbus_name': 'number', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'number', + 'dbus_type': 's', + }, default=None, ) + """Legacy setting that used to help establishing PPP data sessions for GSM- + based modems.""" password: Optional[str] = field( - metadata={'dbus_name': 'password', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'password', + 'dbus_type': 's', + }, default=None, ) + """The password used to authenticate with the network, if required. Many + providers do not require a password, or accept any password. But if + a password is required, it is specified here.""" password_flags: Optional[int] = field( - metadata={'dbus_name': 'password-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'password-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "password" property.""" pin: Optional[str] = field( - metadata={'dbus_name': 'pin', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'pin', + 'dbus_type': 's', + }, default=None, ) + """If the SIM is locked with a PIN it must be unlocked before any other + operations are requested. Specify the PIN here to allow operation + of the device.""" pin_flags: Optional[int] = field( - metadata={'dbus_name': 'pin-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'pin-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "pin" property.""" sim_id: Optional[str] = field( - metadata={'dbus_name': 'sim-id', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'sim-id', + 'dbus_type': 's', + }, default=None, ) + """The SIM card unique identifier (as given by the WWAN management service) + which this connection applies to. If given, the connection will + apply to any device also allowed by "device-id" which contains a SIM + card matching the given identifier.""" sim_operator_id: Optional[str] = field( - metadata={'dbus_name': 'sim-operator-id', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'sim-operator-id', + 'dbus_type': 's', + }, default=None, ) + """A MCC/MNC string like "310260" or "21601" identifying the specific + mobile network operator which this connection applies to. If given, + the connection will apply to any device also allowed by "device-id" + and "sim-id" which contains a SIM card provisioned by the given + operator.""" username: Optional[str] = field( - metadata={'dbus_name': 'username', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'username', + 'dbus_type': 's', + }, default=None, ) + """The username used to authenticate with the network, if required. Many + providers do not require a username, or accept any username. But if + a username is required, it is specified here.""" diff --git a/sdbus_async/networkmanager/settings/hostname.py b/sdbus_async/networkmanager/settings/hostname.py index d8ad4ce..f4e8b77 100644 --- a/sdbus_async/networkmanager/settings/hostname.py +++ b/sdbus_async/networkmanager/settings/hostname.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,18 +12,69 @@ class HostnameSettings(NetworkManagerSettingsMixin): """Hostname settings""" from_dhcp: Optional[int] = field( - metadata={'dbus_name': 'from-dhcp', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'from-dhcp', + 'dbus_type': 'i', + }, default=None, ) + """Whether the system hostname can be determined from DHCP on this + connection. + + When set to NM_TERNARY_DEFAULT (-1), the value from global + configuration is used. If the property doesn't have a value in the + global configuration, NetworkManager assumes the value to be + NM_TERNARY_TRUE (1).""" from_dns_lookup: Optional[int] = field( - metadata={'dbus_name': 'from-dns-lookup', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'from-dns-lookup', + 'dbus_type': 'i', + }, default=None, ) + """Whether the system hostname can be determined from reverse DNS lookup of + addresses on this device. + + When set to NM_TERNARY_DEFAULT (-1), the value from global + configuration is used. If the property doesn't have a value in the + global configuration, NetworkManager assumes the value to be + NM_TERNARY_TRUE (1).""" only_from_default: Optional[int] = field( - metadata={'dbus_name': 'only-from-default', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'only-from-default', + 'dbus_type': 'i', + }, default=None, ) + """If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the + hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only + when the device has the default route for the given address family + (IPv4/IPv6). + + If set to NM_TERNARY_FALSE (0), the hostname can be set from this + device even if it doesn't have the default route. + + When set to NM_TERNARY_DEFAULT (-1), the value from global + configuration is used. If the property doesn't have a value in the + global configuration, NetworkManager assumes the value to be + NM_TERNARY_FALSE (0).""" priority: Optional[int] = field( - metadata={'dbus_name': 'priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'priority', + 'dbus_type': 'i', + }, default=None, ) + """The relative priority of this connection to determine the system + hostname. A lower numerical value is better (higher priority). A + connection with higher priority is considered before connections + with lower priority. + + If the value is zero, it can be overridden by a global value from + NetworkManager configuration. If the property doesn't have a value + in the global configuration, the value is assumed to be 100. + + Negative values have the special effect of excluding other + connections with a greater numerical priority value; so in presence + of at least one negative priority, only connections with the lowest + priority value will be used to determine the hostname.""" diff --git a/sdbus_async/networkmanager/settings/ieee802_1x.py b/sdbus_async/networkmanager/settings/ieee802_1x.py deleted file mode 100644 index 38b4513..0000000 --- a/sdbus_async/networkmanager/settings/ieee802_1x.py +++ /dev/null @@ -1,204 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, -# if possible, please make changes by also updating the script. -from __future__ import annotations -from dataclasses import dataclass, field -from typing import List, Optional -from .base import NetworkManagerSettingsMixin - - -@dataclass -class Ieee8021XSettings(NetworkManagerSettingsMixin): - """IEEE 802.1x Authentication Settings""" - - altsubject_matches: Optional[List[str]] = field( - metadata={'dbus_name': 'altsubject-matches', 'dbus_type': 'as'}, - default=None, - ) - anonymous_identity: Optional[str] = field( - metadata={'dbus_name': 'anonymous-identity', 'dbus_type': 's'}, - default=None, - ) - auth_timeout: Optional[int] = field( - metadata={'dbus_name': 'auth-timeout', 'dbus_type': 'i'}, - default=None, - ) - ca_cert: Optional[bytes] = field( - metadata={'dbus_name': 'ca-cert', 'dbus_type': 'ay'}, - default=None, - ) - ca_cert_password: Optional[str] = field( - metadata={'dbus_name': 'ca-cert-password', 'dbus_type': 's'}, - default=None, - ) - ca_cert_password_flags: Optional[int] = field( - metadata={'dbus_name': 'ca-cert-password-flags', 'dbus_type': 'i'}, - default=None, - ) - ca_path: Optional[str] = field( - metadata={'dbus_name': 'ca-path', 'dbus_type': 's'}, - default=None, - ) - client_cert: Optional[bytes] = field( - metadata={'dbus_name': 'client-cert', 'dbus_type': 'ay'}, - default=None, - ) - client_cert_password: Optional[str] = field( - metadata={'dbus_name': 'client-cert-password', 'dbus_type': 's'}, - default=None, - ) - client_cert_password_flags: Optional[int] = field( - metadata={'dbus_name': 'client-cert-password-flags', 'dbus_type': 'i'}, - default=None, - ) - domain_match: Optional[str] = field( - metadata={'dbus_name': 'domain-match', 'dbus_type': 's'}, - default=None, - ) - domain_suffix_match: Optional[str] = field( - metadata={'dbus_name': 'domain-suffix-match', 'dbus_type': 's'}, - default=None, - ) - eap: Optional[List[str]] = field( - metadata={'dbus_name': 'eap', 'dbus_type': 'as'}, - default=None, - ) - identity: Optional[str] = field( - metadata={'dbus_name': 'identity', 'dbus_type': 's'}, - default=None, - ) - optional: Optional[bool] = field( - metadata={'dbus_name': 'optional', 'dbus_type': 'b'}, - default=False, - ) - pac_file: Optional[str] = field( - metadata={'dbus_name': 'pac-file', 'dbus_type': 's'}, - default=None, - ) - password: Optional[str] = field( - metadata={'dbus_name': 'password', 'dbus_type': 's'}, - default=None, - ) - password_flags: Optional[int] = field( - metadata={'dbus_name': 'password-flags', 'dbus_type': 'i'}, - default=None, - ) - password_raw: Optional[bytes] = field( - metadata={'dbus_name': 'password-raw', 'dbus_type': 'ay'}, - default=None, - ) - password_raw_flags: Optional[int] = field( - metadata={'dbus_name': 'password-raw-flags', 'dbus_type': 'i'}, - default=None, - ) - phase1_auth_flags: Optional[int] = field( - metadata={'dbus_name': 'phase1-auth-flags', 'dbus_type': 'u'}, - default=None, - ) - phase1_fast_provisioning: Optional[str] = field( - metadata={'dbus_name': 'phase1-fast-provisioning', 'dbus_type': 's'}, - default=None, - ) - phase1_peaplabel: Optional[str] = field( - metadata={'dbus_name': 'phase1-peaplabel', 'dbus_type': 's'}, - default=None, - ) - phase1_peapver: Optional[str] = field( - metadata={'dbus_name': 'phase1-peapver', 'dbus_type': 's'}, - default=None, - ) - phase2_altsubject_matches: Optional[List[str]] = field( - metadata={'dbus_name': 'phase2-altsubject-matches', 'dbus_type': 'as'}, - default=None, - ) - phase2_auth: Optional[str] = field( - metadata={'dbus_name': 'phase2-auth', 'dbus_type': 's'}, - default=None, - ) - phase2_autheap: Optional[str] = field( - metadata={'dbus_name': 'phase2-autheap', 'dbus_type': 's'}, - default=None, - ) - phase2_ca_cert: Optional[bytes] = field( - metadata={'dbus_name': 'phase2-ca-cert', 'dbus_type': 'ay'}, - default=None, - ) - phase2_ca_cert_password: Optional[str] = field( - metadata={'dbus_name': 'phase2-ca-cert-password', 'dbus_type': 's'}, - default=None, - ) - phase2_ca_cert_password_flags: Optional[int] = field( - metadata={'dbus_name': 'phase2-ca-cert-password-flags', - 'dbus_type': 'i'}, - default=None, - ) - phase2_ca_path: Optional[str] = field( - metadata={'dbus_name': 'phase2-ca-path', 'dbus_type': 's'}, - default=None, - ) - phase2_client_cert: Optional[bytes] = field( - metadata={'dbus_name': 'phase2-client-cert', 'dbus_type': 'ay'}, - default=None, - ) - phase2_client_cert_password: Optional[str] = field( - metadata={'dbus_name': 'phase2-client-cert-password', 'dbus_type': 's'}, - default=None, - ) - phase2_client_cert_password_flags: Optional[int] = field( - metadata={'dbus_name': 'phase2-client-cert-password-flags', - 'dbus_type': 'i'}, - default=None, - ) - phase2_domain_match: Optional[str] = field( - metadata={'dbus_name': 'phase2-domain-match', 'dbus_type': 's'}, - default=None, - ) - phase2_domain_suffix_match: Optional[str] = field( - metadata={'dbus_name': 'phase2-domain-suffix-match', 'dbus_type': 's'}, - default=None, - ) - phase2_private_key: Optional[bytes] = field( - metadata={'dbus_name': 'phase2-private-key', 'dbus_type': 'ay'}, - default=None, - ) - phase2_private_key_password: Optional[str] = field( - metadata={'dbus_name': 'phase2-private-key-password', 'dbus_type': 's'}, - default=None, - ) - phase2_private_key_password_flags: Optional[int] = field( - metadata={'dbus_name': 'phase2-private-key-password-flags', - 'dbus_type': 'i'}, - default=None, - ) - phase2_subject_match: Optional[str] = field( - metadata={'dbus_name': 'phase2-subject-match', 'dbus_type': 's'}, - default=None, - ) - pin: Optional[str] = field( - metadata={'dbus_name': 'pin', 'dbus_type': 's'}, - default=None, - ) - pin_flags: Optional[int] = field( - metadata={'dbus_name': 'pin-flags', 'dbus_type': 'i'}, - default=None, - ) - private_key: Optional[bytes] = field( - metadata={'dbus_name': 'private-key', 'dbus_type': 'ay'}, - default=None, - ) - private_key_password: Optional[str] = field( - metadata={'dbus_name': 'private-key-password', 'dbus_type': 's'}, - default=None, - ) - private_key_password_flags: Optional[int] = field( - metadata={'dbus_name': 'private-key-password-flags', 'dbus_type': 'i'}, - default=None, - ) - subject_match: Optional[str] = field( - metadata={'dbus_name': 'subject-match', 'dbus_type': 's'}, - default=None, - ) - system_ca_certs: Optional[bool] = field( - metadata={'dbus_name': 'system-ca-certs', 'dbus_type': 'b'}, - default=False, - ) diff --git a/sdbus_async/networkmanager/settings/infiniband.py b/sdbus_async/networkmanager/settings/infiniband.py index 0cc31bc..a144095 100644 --- a/sdbus_async/networkmanager/settings/infiniband.py +++ b/sdbus_async/networkmanager/settings/infiniband.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,22 +12,50 @@ class InfinibandSettings(NetworkManagerSettingsMixin): """Infiniband Settings""" mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, this connection will only apply to the IPoIB device whose + permanent MAC address matches. This property does not change the MAC + address of the device (i.e. MAC spoofing).""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple frames.""" p_key: Optional[int] = field( - metadata={'dbus_name': 'p-key', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'p-key', + 'dbus_type': 'i', + }, default=None, ) + """The InfiniBand P_Key to use for this device. A value of -1 means to use + the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a + 16-bit unsigned integer, whose high bit is set if it is a "full + membership" P_Key.""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """The interface name of the parent device of this device. Normally NULL, + but if the "p_key" property is set, then you must specify the base + device by setting either this property or "mac-address".""" transport_mode: Optional[str] = field( - metadata={'dbus_name': 'transport-mode', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'transport-mode', + 'dbus_type': 's', + }, default=None, ) + """The IP-over-InfiniBand transport mode. Either "datagram" or "connected".""" diff --git a/sdbus_async/networkmanager/settings/ip_tunnel.py b/sdbus_async/networkmanager/settings/ip_tunnel.py index 48e81ec..da2f0ad 100644 --- a/sdbus_async/networkmanager/settings/ip_tunnel.py +++ b/sdbus_async/networkmanager/settings/ip_tunnel.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,54 +12,125 @@ class IpTunnelSettings(NetworkManagerSettingsMixin): """IP Tunneling Settings""" encapsulation_limit: Optional[int] = field( - metadata={'dbus_name': 'encapsulation-limit', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'encapsulation-limit', + 'dbus_type': 'u', + }, default=None, ) + """How many additional levels of encapsulation are permitted to be + prepended to packets. This property applies only to IPv6 tunnels.""" flags: Optional[int] = field( - metadata={'dbus_name': 'flags', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'flags', + 'dbus_type': 'u', + }, default=None, ) + """Tunnel flags. Currently, the following values are supported: + NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), + NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), + NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), + NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), + NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), + NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only + for IPv6 tunnels.""" flow_label: Optional[int] = field( - metadata={'dbus_name': 'flow-label', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'flow-label', + 'dbus_type': 'u', + }, default=None, ) + """The flow label to assign to tunnel packets. This property applies only + to IPv6 tunnels.""" input_key: Optional[str] = field( - metadata={'dbus_name': 'input-key', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'input-key', + 'dbus_type': 's', + }, default=None, ) + """The key used for tunnel input packets; the property is valid only for + certain tunnel modes (GRE, IP6GRE). If empty, no key is used.""" local: Optional[str] = field( - metadata={'dbus_name': 'local', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'local', + 'dbus_type': 's', + }, default=None, ) + """The local endpoint of the tunnel; the value can be empty, otherwise it + must contain an IPv4 or IPv6 address.""" mode: Optional[int] = field( - metadata={'dbus_name': 'mode', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mode', + 'dbus_type': 'u', + }, default=None, ) + """The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or + NM_IP_TUNNEL_MODE_GRE (2).""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple fragments.""" output_key: Optional[str] = field( - metadata={'dbus_name': 'output-key', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'output-key', + 'dbus_type': 's', + }, default=None, ) + """The key used for tunnel output packets; the property is valid only for + certain tunnel modes (GRE, IP6GRE). If empty, no key is used.""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name or parent connection UUID + the new device will be bound to so that tunneled packets will only + be routed via that interface.""" path_mtu_discovery: Optional[bool] = field( - metadata={'dbus_name': 'path-mtu-discovery', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'path-mtu-discovery', + 'dbus_type': 'b', + }, + default=None, ) + """Whether to enable Path MTU Discovery on this tunnel.""" remote: Optional[str] = field( - metadata={'dbus_name': 'remote', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'remote', + 'dbus_type': 's', + }, default=None, ) + """The remote endpoint of the tunnel; the value must contain an IPv4 or + IPv6 address.""" tos: Optional[int] = field( - metadata={'dbus_name': 'tos', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'tos', + 'dbus_type': 'u', + }, default=None, ) + """The type of service (IPv4) or traffic class (IPv6) field to be set on + tunneled packets.""" ttl: Optional[int] = field( - metadata={'dbus_name': 'ttl', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'ttl', + 'dbus_type': 'u', + }, default=None, ) + """The TTL to assign to tunneled packets. 0 is a special value meaning that + packets inherit the TTL value.""" diff --git a/sdbus_async/networkmanager/settings/ipv4.py b/sdbus_async/networkmanager/settings/ipv4.py index 5a383a3..b2b91f2 100644 --- a/sdbus_async/networkmanager/settings/ipv4.py +++ b/sdbus_async/networkmanager/settings/ipv4.py @@ -1,11 +1,11 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin -from .datatypes import AddressData, RouteData +from .datatypes import AddressData, RouteData, RoutingRules @dataclass @@ -13,115 +13,533 @@ class Ipv4Settings(NetworkManagerSettingsMixin): """IPv4 Settings""" address_data: Optional[List[AddressData]] = field( - metadata={'dbus_name': 'address-data', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': AddressData}, + metadata={ + 'dbus_name': 'address-data', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': AddressData, + }, default=None, ) + """Array of IPv4 addresses. Each address dictionary contains at least + 'address' and 'prefix' entries, containing the IP address as a + string, and the prefix length as a uint32. Additional attributes may + also exist on some addresses.""" addresses: Optional[List[List[int]]] = field( - metadata={'dbus_name': 'addresses', 'dbus_type': 'aau'}, + metadata={ + 'dbus_name': 'addresses', + 'dbus_type': 'aau', + }, default=None, ) + """Array of IP addresses.""" dad_timeout: Optional[int] = field( - metadata={'dbus_name': 'dad-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dad-timeout', + 'dbus_type': 'i', + }, default=None, ) + """Timeout in milliseconds used to check for the presence of duplicate IP + addresses on the network. If an address conflict is detected, the + activation will fail. A zero value means that no duplicate address + detection is performed, -1 means the default value (either + configuration ipvx.dad-timeout override or zero). A value greater + than zero is a timeout in milliseconds. + + The property is currently implemented only for IPv4.""" dhcp_client_id: Optional[str] = field( - metadata={'dbus_name': 'dhcp-client-id', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-client-id', + 'dbus_type': 's', + }, default=None, ) + """A string sent to the DHCP server to identify the local machine which the + DHCP server may use to customize the DHCP lease and options. When + the property is a hex string ('aa:bb:cc') it is interpreted as a + binary client ID, in which case the first byte is assumed to be the + 'type' field as per RFC 2132 section 9.14 and the remaining bytes + may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is + the Ethernet ARP type and the rest is a MAC address). If the + property is not a hex string it is considered as a non-hardware- + address client ID and the 'type' field is set to 0. + + The special values "mac" and "perm-mac" are supported, which use the + current or permanent MAC address of the device to generate a client + identifier with type ethernet (01). Currently, these options only + work for ethernet type of links. + + The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" + property as an RFC4361-compliant client identifier. As IAID it uses + "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset. + + The special value "duid" generates a RFC4361-compliant client + identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by + hashing /etc/machine-id. + + The special value "stable" is supported to generate a type 0 client + identifier based on the stable-id (see connection.stable-id) and a + per-host key. If you set the stable-id, you may want to include the + "${DEVICE}" or "${MAC}" specifier to get a per-device key. + + If unset, a globally configured default is used. If still unset, the + default depends on the DHCP plugin.""" dhcp_fqdn: Optional[str] = field( - metadata={'dbus_name': 'dhcp-fqdn', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-fqdn', + 'dbus_type': 's', + }, default=None, ) + """If the "dhcp-send-hostname" property is TRUE, then the specified FQDN + will be sent to the DHCP server when acquiring a lease. This + property and "dhcp-hostname" are mutually exclusive and cannot be + set at the same time.""" dhcp_hostname: Optional[str] = field( - metadata={'dbus_name': 'dhcp-hostname', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-hostname', + 'dbus_type': 's', + }, default=None, ) + """If the "dhcp-send-hostname" property is TRUE, then the specified name + will be sent to the DHCP server when acquiring a lease. This + property and "dhcp-fqdn" are mutually exclusive and cannot be set at + the same time.""" dhcp_hostname_flags: Optional[int] = field( - metadata={'dbus_name': 'dhcp-hostname-flags', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'dhcp-hostname-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags for the DHCP hostname and FQDN. + + Currently, this property only includes flags to control the FQDN + flags set in the DHCP FQDN option. Supported FQDN flags are + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), + NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and + NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is + set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the + DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is + set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the + standard FQDN flags are set in the request: + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), + NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. + + When this property is set to the default value + NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in + NetworkManager configuration. If that value is unset or also + NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags + described above are sent in the DHCP requests.""" dhcp_iaid: Optional[str] = field( - metadata={'dbus_name': 'dhcp-iaid', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-iaid', + 'dbus_type': 's', + }, default=None, ) + """A string containing the "Identity Association Identifier" (IAID) used by + the DHCP client. The property is a 32-bit decimal value or a special + value among "mac", "perm-mac", "ifname" and "stable". When set to + "mac" (or "perm-mac"), the last 4 bytes of the current (or + permanent) MAC address are used as IAID. When set to "ifname", the + IAID is computed by hashing the interface name. The special value + "stable" can be used to generate an IAID based on the stable-id (see + connection.stable-id), a per-host key and the interface name. When + the property is unset, the value from global configuration is used; + if no global default is set then the IAID is assumed to be "ifname". + Note that at the moment this property is ignored for IPv6 by + dhclient, which always derives the IAID from the MAC address.""" dhcp_reject_servers: Optional[List[str]] = field( - metadata={'dbus_name': 'dhcp-reject-servers', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'dhcp-reject-servers', + 'dbus_type': 'as', + }, default=None, ) + """Array of servers from which DHCP offers must be rejected. This property + is useful to avoid getting a lease from misconfigured or rogue + servers. + + For DHCPv4, each element must be an IPv4 address, optionally + followed by a slash and a prefix length (e.g. "192.168.122.0/24"). + + This property is currently not implemented for DHCPv6.""" dhcp_send_hostname: Optional[bool] = field( - metadata={'dbus_name': 'dhcp-send-hostname', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'dhcp-send-hostname', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, a hostname is sent to the DHCP server when acquiring a lease. + Some DHCP servers use this hostname to update DNS databases, + essentially providing a static hostname for the computer. If the + "dhcp-hostname" property is NULL and this property is TRUE, the + current persistent hostname of the computer is sent.""" dhcp_timeout: Optional[int] = field( - metadata={'dbus_name': 'dhcp-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dhcp-timeout', + 'dbus_type': 'i', + }, default=None, ) + """A timeout for a DHCP transaction in seconds. If zero (the default), a + globally configured default is used. If still unspecified, a device + specific timeout is used (usually 45 seconds). + + Set to 2147483647 (MAXINT32) for infinity.""" dhcp_vendor_class_identifier: Optional[str] = field( - metadata={'dbus_name': 'dhcp-vendor-class-identifier', - 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-vendor-class-identifier', + 'dbus_type': 's', + }, default=None, ) + """The Vendor Class Identifier DHCP option (60). Special characters in the + data string may be escaped using C-style escapes, nevertheless this + property cannot contain nul bytes. If the per-profile value is + unspecified (the default), a global connection default gets + consulted. If still unspecified, the DHCP option is not sent to the + server. + + Since 1.28""" dns: Optional[List[int]] = field( - metadata={'dbus_name': 'dns', 'dbus_type': 'au'}, + metadata={ + 'dbus_name': 'dns', + 'dbus_type': 'au', + }, default=None, ) + """Array of IP addresses of DNS servers. + + For DoT (DNS over TLS), the SNI server name can be specified by + appending "#example.com" to the IP address of the DNS server. This + currently only has effect when using systemd-resolved.""" + dns_data: Optional[List[str]] = field( + metadata={ + 'dbus_name': 'dns-data', + 'dbus_type': 'as', + }, + default=None, + ) + """Array of DNS name servers. This replaces the deprecated "dns" property. + Each name server can also contain a DoT server name.""" dns_options: Optional[List[str]] = field( - metadata={'dbus_name': 'dns-options', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'dns-options', + 'dbus_type': 'as', + }, default=None, ) + """Array of DNS options as described in man 5 resolv.conf. + + NULL means that the options are unset and left at the default. In + this case NetworkManager will use default options. This is distinct + from an empty list of properties. + + The currently supported options are "attempts", "debug", "edns0", + "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", + "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single- + request", "single-request-reopen", "timeout", "trust-ad", "use-vc". + + The "trust-ad" setting is only honored if the profile contributes + name servers to resolv.conf, and if all contributing profiles have + "trust-ad" enabled. + + When using a caching DNS plugin (dnsmasq or systemd-resolved in + NetworkManager.conf) then "edns0" and "trust-ad" are automatically + added.""" dns_priority: Optional[int] = field( - metadata={'dbus_name': 'dns-priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dns-priority', + 'dbus_type': 'i', + }, default=None, ) + """DNS servers priority. + + The relative priority for DNS servers specified by this setting. A + lower numerical value is better (higher priority). + + Negative values have the special effect of excluding other + configurations with a greater numerical priority value; so in + presence of at least one negative priority, only DNS servers from + connections with the lowest priority value will be used. To avoid + all DNS leaks, set the priority of the profile that should be used + to the most negative value of all active connections profiles. + + Zero selects a globally configured default value. If the latter is + missing or zero too, it defaults to 50 for VPNs (including + WireGuard) and 100 for other connections. + + Note that the priority is to order DNS settings for multiple active + connections. It does not disambiguate multiple DNS servers within + the same connection profile. + + When multiple devices have configurations with the same priority, + VPNs will be considered first, then devices with the best (lowest + metric) default route and then all other devices. + + When using dns=default, servers with higher priority will be on top + of resolv.conf. To prioritize a given server over another one within + the same connection, just specify them in the desired order. Note + that commonly the resolver tries name servers in /etc/resolv.conf in + the order listed, proceeding with the next server in the list on + failure. See for example the "rotate" option of the dns-options + setting. If there are any negative DNS priorities, then only name + servers from the devices with that lowest priority will be + considered. + + When using a DNS resolver that supports Conditional Forwarding or + Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each + connection is used to query domains in its search list. The search + domains determine which name servers to ask, and the DNS priority is + used to prioritize name servers based on the domain. Queries for + domains not present in any search list are routed through + connections having the '~.' special wildcard domain, which is added + automatically to connections with the default route (or can be added + manually). When multiple connections specify the same domain, the + one with the best priority (lowest numerical value) wins. If a sub + domain is configured on another interface it will be accepted + regardless the priority, unless parent domain on the other interface + has a negative priority, which causes the sub domain to be shadowed. + With Split DNS one can avoid undesired DNS leaks by properly + configuring DNS priorities and the search domains, so that only name + servers of the desired interface are configured.""" dns_search: Optional[List[str]] = field( - metadata={'dbus_name': 'dns-search', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'dns-search', + 'dbus_type': 'as', + }, default=None, ) + """List of DNS search domains. Domains starting with a tilde ('~') are + considered 'routing' domains and are used only to decide the + interface over which a query must be forwarded; they are not used to + complete unqualified host names. + + When using a DNS plugin that supports Conditional Forwarding or + Split DNS, then the search domains specify which name servers to + query. This makes the behavior different from running with plain + /etc/resolv.conf. For more information see also the dns-priority + setting. + + When set on a profile that also enabled DHCP, the DNS search list + received automatically (option 119 for DHCPv4 and option 24 for + DHCPv6) gets merged with the manual list. This can be prevented by + setting "ignore-auto-dns". Note that if no DNS searches are + configured, the fallback will be derived from the domain from DHCP + (option 15).""" gateway: Optional[str] = field( - metadata={'dbus_name': 'gateway', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'gateway', + 'dbus_type': 's', + }, default=None, ) + """The gateway associated with this configuration. This is only meaningful + if "addresses" is also set. + + Setting the gateway causes NetworkManager to configure a standard + default route with the gateway as next hop. This is ignored if + "never-default" is set. An alternative is to configure the default + route explicitly with a manual route and /0 as prefix length. + + Note that the gateway usually conflicts with routing that + NetworkManager configures for WireGuard interfaces, so usually it + should not be set in that case. See "ip4-auto-default-route".""" ignore_auto_dns: Optional[bool] = field( - metadata={'dbus_name': 'ignore-auto-dns', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'ignore-auto-dns', + 'dbus_type': 'b', + }, + default=None, ) + """When "method" is set to "auto" and this property to TRUE, automatically + configured name servers and search domains are ignored and only name + servers and search domains specified in the "dns" and "dns-search" + properties, if any, are used.""" ignore_auto_routes: Optional[bool] = field( - metadata={'dbus_name': 'ignore-auto-routes', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'ignore-auto-routes', + 'dbus_type': 'b', + }, + default=None, ) + """When "method" is set to "auto" and this property to TRUE, automatically + configured routes are ignored and only routes specified in the + "routes" property, if any, are used.""" + link_local: Optional[int] = field( + metadata={ + 'dbus_name': 'link-local', + 'dbus_type': 'i', + }, + default=None, + ) + """Enable and disable the IPv4 link-local configuration independently of + the ipv4.method configuration. This allows a link-local address + (169.254.x.y/16) to be obtained in addition to other addresses, such + as those manually configured or obtained from a DHCP server. + + When set to "auto", the value is dependent on "ipv4.method". When + set to "default", it honors the global connection default, before + falling back to "auto". Note that if "ipv4.method" is "disabled", + then link local addressing is always disabled too. The default is + "default". + + Since 1.40""" may_fail: Optional[bool] = field( - metadata={'dbus_name': 'may-fail', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'may-fail', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, allow overall network configuration to proceed even if the + configuration specified by this property times out. Note that at + least one IP configuration must succeed or overall network + configuration will still fail. For example, in IPv6-only networks, + setting this property to TRUE on the NMSettingIP4Config allows the + overall network configuration to succeed if IPv4 configuration fails + but IPv6 configuration completes successfully.""" method: Optional[str] = field( - metadata={'dbus_name': 'method', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'method', + 'dbus_type': 's', + }, default=None, ) + """IP configuration method. + + NMSettingIP4Config and NMSettingIP6Config both support "disabled", + "auto", "manual", and "link-local". See the subclass-specific + documentation for other values. + + In general, for the "auto" method, properties such as "dns" and + "routes" specify information that is added on to the information + returned from automatic configuration. The "ignore-auto-routes" and + "ignore-auto-dns" properties modify this behavior. + + For methods that imply no upstream network, such as "shared" or + "link-local", these properties must be empty. + + For IPv4 method "shared", the IP subnet can be configured by adding + one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note + that the shared method must be configured on the interface which + shares the internet to a subnet, not on the uplink which is shared.""" never_default: Optional[bool] = field( - metadata={'dbus_name': 'never-default', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'never-default', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, this connection will never be the default connection for this + IP type, meaning it will never be assigned the default route by + NetworkManager.""" required_timeout: Optional[int] = field( - metadata={'dbus_name': 'required-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'required-timeout', + 'dbus_type': 'i', + }, default=None, ) + """The minimum time interval in milliseconds for which dynamic IP + configuration should be tried before the connection succeeds. + + This property is useful for example if both IPv4 and IPv6 are + enabled and are allowed to fail. Normally the connection succeeds as + soon as one of the two address families completes; by setting a + required timeout for e.g. IPv4, one can ensure that even if IP6 + succeeds earlier than IPv4, NetworkManager waits some time for IPv4 + before the connection becomes active. + + Note that if "may-fail" is FALSE for the same address family, this + property has no effect as NetworkManager needs to wait for the full + DHCP timeout. + + A zero value means that no required timeout is present, -1 means the + default value (either configuration ipvx.required-timeout override + or zero).""" route_data: Optional[List[RouteData]] = field( - metadata={'dbus_name': 'route-data', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': RouteData}, + metadata={ + 'dbus_name': 'route-data', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': RouteData, + }, default=None, ) + """Array of IPv4 routes. Each route dictionary contains at least 'dest' and + 'prefix' entries, containing the destination IP address as a string, + and the prefix length as a uint32. Most routes will also have a + 'next-hop' entry, containing the next hop IP address as a string. If + the route has a 'metric' entry (containing a uint32), that will be + used as the metric for the route (otherwise NM will pick a default + value appropriate to the device). Additional attributes may also + exist on some routes.""" route_metric: Optional[int] = field( - metadata={'dbus_name': 'route-metric', 'dbus_type': 'x'}, + metadata={ + 'dbus_name': 'route-metric', + 'dbus_type': 'x', + }, default=None, ) + """The default metric for routes that don't explicitly specify a metric. + The default value -1 means that the metric is chosen automatically + based on the device type. The metric applies to dynamic routes, + manual (static) routes that don't have an explicit metric setting, + address prefix routes, and the default route. Note that for IPv6, + the kernel accepts zero (0) but coerces it to 1024 (user default). + Hence, setting this property to zero effectively mean setting it to + 1024. For IPv4, zero is a regular value for the metric.""" route_table: Optional[int] = field( - metadata={'dbus_name': 'route-table', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'route-table', + 'dbus_type': 'u', + }, default=None, ) + """Enable policy routing (source routing) and set the routing table used + when adding routes. + + This affects all routes, including device-routes, IPv4LL, DHCP, + SLAAC, default-routes and static routes. But note that static routes + can individually overwrite the setting by explicitly specifying a + non-zero routing table. + + If the table setting is left at zero, it is eligible to be + overwritten via global configuration. If the property is zero even + after applying the global configuration value, policy routing is + disabled for the address family of this connection. + + Policy routing disabled means that NetworkManager will add all + routes to the main table (except static routes that explicitly + configure a different table). Additionally, NetworkManager will not + delete any extraneous routes from tables except the main table. This + is to preserve backward compatibility for users who manage routing + tables outside of NetworkManager.""" routes: Optional[List[List[int]]] = field( - metadata={'dbus_name': 'routes', 'dbus_type': 'aau'}, + metadata={ + 'dbus_name': 'routes', + 'dbus_type': 'aau', + }, + default=None, + ) + """Array of IP routes.""" + routing_rules: Optional[List[RoutingRules]] = field( + metadata={ + 'dbus_name': 'routing-rules', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': RoutingRules, + }, default=None, ) + """Array of dictionaries for routing rules. Each routing rule supports the + following options: action (y), dport-end (q), dport-start (q), + family (i), from (s), from-len (y), fwmark (u), fwmask (u), iifname + (s), invert (b), ipproto (s), oifname (s), priority (u), sport-end + (q), sport-start (q), supress-prefixlength (i), table (u), to (s), + tos (y), to-len (y), range-end (u), range-start (u).""" diff --git a/sdbus_async/networkmanager/settings/ipv6.py b/sdbus_async/networkmanager/settings/ipv6.py index 1456d78..7f69c51 100644 --- a/sdbus_async/networkmanager/settings/ipv6.py +++ b/sdbus_async/networkmanager/settings/ipv6.py @@ -1,11 +1,11 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin -from .datatypes import AddressData, RouteData +from .datatypes import AddressData, RouteData, RoutingRules @dataclass @@ -13,114 +13,577 @@ class Ipv6Settings(NetworkManagerSettingsMixin): """IPv6 Settings""" addr_gen_mode: Optional[int] = field( - metadata={'dbus_name': 'addr-gen-mode', 'dbus_type': 'i'}, - default=1, + metadata={ + 'dbus_name': 'addr-gen-mode', + 'dbus_type': 'i', + }, + default=None, ) + """Configure method for creating the address for use with RFC4862 IPv6 + Stateless Address Autoconfiguration. The permitted values are: + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3). + + If the property is set to EUI64, the addresses will be generated + using the interface tokens derived from hardware address. This makes + the host part of the address to stay constant, making it possible to + track host's presence when it changes networks. The address changes + when the interface hardware is replaced. + + The value of stable-privacy enables use of cryptographically secure + hash of a secret host-specific key along with the connection's + stable-id and the network address as specified by RFC7217. This + makes it impossible to use the address track host's presence, and + makes the address stable when the network interface hardware is + replaced. + + The special values "default" and "default-or-eui64" will fallback to + the global connection default in as documented in + NetworkManager.conf(5) manual. If the global default is not + specified, the fallback value is "stable-privacy" or "eui64", + respectively. + + For libnm, the property defaults to "default" since 1.40. Previously + it defaulted to "stable-privacy". On D-Bus, the absence of an addr- + gen-mode setting equals "default". For keyfile plugin, the absence + of the setting on disk means "default-or-eui64" so that the property + doesn't change on upgrade from older versions. + + Note that this setting is distinct from the Privacy Extensions as + configured by "ip6-privacy" property and it does not affect the + temporary addresses configured with this option.""" address_data: Optional[List[AddressData]] = field( - metadata={'dbus_name': 'address-data', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': AddressData}, + metadata={ + 'dbus_name': 'address-data', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': AddressData, + }, default=None, ) + """Array of IPv6 addresses. Each address dictionary contains at least + 'address' and 'prefix' entries, containing the IP address as a + string, and the prefix length as a uint32. Additional attributes may + also exist on some addresses.""" dad_timeout: Optional[int] = field( - metadata={'dbus_name': 'dad-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dad-timeout', + 'dbus_type': 'i', + }, default=None, ) + """Timeout in milliseconds used to check for the presence of duplicate IP + addresses on the network. If an address conflict is detected, the + activation will fail. A zero value means that no duplicate address + detection is performed, -1 means the default value (either + configuration ipvx.dad-timeout override or zero). A value greater + than zero is a timeout in milliseconds. + + The property is currently implemented only for IPv4.""" dhcp_duid: Optional[str] = field( - metadata={'dbus_name': 'dhcp-duid', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-duid', + 'dbus_type': 's', + }, default=None, ) + """A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp + client to identify itself to DHCPv6 servers (RFC 3315). The DUID is + carried in the Client Identifier option. If the property is a hex + string ('aa:bb:cc') it is interpreted as a binary DUID and filled as + an opaque value in the Client Identifier option. + + The special value "lease" will retrieve the DUID previously used + from the lease file belonging to the connection. If no DUID is found + and "dhclient" is the configured dhcp client, the DUID is searched + in the system-wide dhclient lease file. If still no DUID is found, + or another dhcp client is used, a global and permanent DUID-UUID + (RFC 6355) will be generated based on the machine-id. + + The special values "llt" and "ll" will generate a DUID of type LLT + or LL (see RFC 3315) based on the current MAC address of the device. + In order to try providing a stable DUID-LLT, the time field will + contain a constant timestamp that is used globally (for all + profiles) and persisted to disk. + + The special values "stable-llt", "stable-ll" and "stable-uuid" will + generate a DUID of the corresponding type, derived from the + connection's stable-id and a per-host unique key. You may want to + include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in + case this profile gets activated on multiple devices. So, the link- + layer address of "stable-ll" and "stable-llt" will be a generated + address derived from the stable id. The DUID-LLT time value in the + "stable-llt" option will be picked among a static timespan of three + years (the upper bound of the interval is the same constant + timestamp used in "llt"). + + When the property is unset, the global value provided for + "ipv6.dhcp-duid" is used. If no global value is provided, the + default "lease" value is assumed.""" dhcp_hostname: Optional[str] = field( - metadata={'dbus_name': 'dhcp-hostname', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-hostname', + 'dbus_type': 's', + }, default=None, ) + """If the "dhcp-send-hostname" property is TRUE, then the specified name + will be sent to the DHCP server when acquiring a lease. This + property and "dhcp-fqdn" are mutually exclusive and cannot be set at + the same time.""" dhcp_hostname_flags: Optional[int] = field( - metadata={'dbus_name': 'dhcp-hostname-flags', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'dhcp-hostname-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags for the DHCP hostname and FQDN. + + Currently, this property only includes flags to control the FQDN + flags set in the DHCP FQDN option. Supported FQDN flags are + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), + NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and + NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is + set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the + DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is + set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the + standard FQDN flags are set in the request: + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), + NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. + + When this property is set to the default value + NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in + NetworkManager configuration. If that value is unset or also + NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags + described above are sent in the DHCP requests.""" dhcp_iaid: Optional[str] = field( - metadata={'dbus_name': 'dhcp-iaid', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'dhcp-iaid', + 'dbus_type': 's', + }, default=None, ) + """A string containing the "Identity Association Identifier" (IAID) used by + the DHCP client. The property is a 32-bit decimal value or a special + value among "mac", "perm-mac", "ifname" and "stable". When set to + "mac" (or "perm-mac"), the last 4 bytes of the current (or + permanent) MAC address are used as IAID. When set to "ifname", the + IAID is computed by hashing the interface name. The special value + "stable" can be used to generate an IAID based on the stable-id (see + connection.stable-id), a per-host key and the interface name. When + the property is unset, the value from global configuration is used; + if no global default is set then the IAID is assumed to be "ifname". + Note that at the moment this property is ignored for IPv6 by + dhclient, which always derives the IAID from the MAC address.""" dhcp_reject_servers: Optional[List[str]] = field( - metadata={'dbus_name': 'dhcp-reject-servers', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'dhcp-reject-servers', + 'dbus_type': 'as', + }, default=None, ) + """Array of servers from which DHCP offers must be rejected. This property + is useful to avoid getting a lease from misconfigured or rogue + servers. + + For DHCPv4, each element must be an IPv4 address, optionally + followed by a slash and a prefix length (e.g. "192.168.122.0/24"). + + This property is currently not implemented for DHCPv6.""" dhcp_send_hostname: Optional[bool] = field( - metadata={'dbus_name': 'dhcp-send-hostname', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'dhcp-send-hostname', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, a hostname is sent to the DHCP server when acquiring a lease. + Some DHCP servers use this hostname to update DNS databases, + essentially providing a static hostname for the computer. If the + "dhcp-hostname" property is NULL and this property is TRUE, the + current persistent hostname of the computer is sent.""" dhcp_timeout: Optional[int] = field( - metadata={'dbus_name': 'dhcp-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dhcp-timeout', + 'dbus_type': 'i', + }, default=None, ) + """A timeout for a DHCP transaction in seconds. If zero (the default), a + globally configured default is used. If still unspecified, a device + specific timeout is used (usually 45 seconds). + + Set to 2147483647 (MAXINT32) for infinity.""" dns: Optional[List[bytes]] = field( - metadata={'dbus_name': 'dns', 'dbus_type': 'aay'}, + metadata={ + 'dbus_name': 'dns', + 'dbus_type': 'aay', + }, default=None, ) + """Array of IP addresses of DNS servers. + + For DoT (DNS over TLS), the SNI server name can be specified by + appending "#example.com" to the IP address of the DNS server. This + currently only has effect when using systemd-resolved.""" + dns_data: Optional[List[str]] = field( + metadata={ + 'dbus_name': 'dns-data', + 'dbus_type': 'as', + }, + default=None, + ) + """Array of DNS name servers. This replaces the deprecated "dns" property. + Each name server can also contain a DoT server name.""" dns_options: Optional[List[str]] = field( - metadata={'dbus_name': 'dns-options', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'dns-options', + 'dbus_type': 'as', + }, default=None, ) + """Array of DNS options as described in man 5 resolv.conf. + + NULL means that the options are unset and left at the default. In + this case NetworkManager will use default options. This is distinct + from an empty list of properties. + + The currently supported options are "attempts", "debug", "edns0", + "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", + "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single- + request", "single-request-reopen", "timeout", "trust-ad", "use-vc". + + The "trust-ad" setting is only honored if the profile contributes + name servers to resolv.conf, and if all contributing profiles have + "trust-ad" enabled. + + When using a caching DNS plugin (dnsmasq or systemd-resolved in + NetworkManager.conf) then "edns0" and "trust-ad" are automatically + added.""" dns_priority: Optional[int] = field( - metadata={'dbus_name': 'dns-priority', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'dns-priority', + 'dbus_type': 'i', + }, default=None, ) + """DNS servers priority. + + The relative priority for DNS servers specified by this setting. A + lower numerical value is better (higher priority). + + Negative values have the special effect of excluding other + configurations with a greater numerical priority value; so in + presence of at least one negative priority, only DNS servers from + connections with the lowest priority value will be used. To avoid + all DNS leaks, set the priority of the profile that should be used + to the most negative value of all active connections profiles. + + Zero selects a globally configured default value. If the latter is + missing or zero too, it defaults to 50 for VPNs (including + WireGuard) and 100 for other connections. + + Note that the priority is to order DNS settings for multiple active + connections. It does not disambiguate multiple DNS servers within + the same connection profile. + + When multiple devices have configurations with the same priority, + VPNs will be considered first, then devices with the best (lowest + metric) default route and then all other devices. + + When using dns=default, servers with higher priority will be on top + of resolv.conf. To prioritize a given server over another one within + the same connection, just specify them in the desired order. Note + that commonly the resolver tries name servers in /etc/resolv.conf in + the order listed, proceeding with the next server in the list on + failure. See for example the "rotate" option of the dns-options + setting. If there are any negative DNS priorities, then only name + servers from the devices with that lowest priority will be + considered. + + When using a DNS resolver that supports Conditional Forwarding or + Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each + connection is used to query domains in its search list. The search + domains determine which name servers to ask, and the DNS priority is + used to prioritize name servers based on the domain. Queries for + domains not present in any search list are routed through + connections having the '~.' special wildcard domain, which is added + automatically to connections with the default route (or can be added + manually). When multiple connections specify the same domain, the + one with the best priority (lowest numerical value) wins. If a sub + domain is configured on another interface it will be accepted + regardless the priority, unless parent domain on the other interface + has a negative priority, which causes the sub domain to be shadowed. + With Split DNS one can avoid undesired DNS leaks by properly + configuring DNS priorities and the search domains, so that only name + servers of the desired interface are configured.""" dns_search: Optional[List[str]] = field( - metadata={'dbus_name': 'dns-search', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'dns-search', + 'dbus_type': 'as', + }, default=None, ) + """List of DNS search domains. Domains starting with a tilde ('~') are + considered 'routing' domains and are used only to decide the + interface over which a query must be forwarded; they are not used to + complete unqualified host names. + + When using a DNS plugin that supports Conditional Forwarding or + Split DNS, then the search domains specify which name servers to + query. This makes the behavior different from running with plain + /etc/resolv.conf. For more information see also the dns-priority + setting. + + When set on a profile that also enabled DHCP, the DNS search list + received automatically (option 119 for DHCPv4 and option 24 for + DHCPv6) gets merged with the manual list. This can be prevented by + setting "ignore-auto-dns". Note that if no DNS searches are + configured, the fallback will be derived from the domain from DHCP + (option 15).""" gateway: Optional[str] = field( - metadata={'dbus_name': 'gateway', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'gateway', + 'dbus_type': 's', + }, default=None, ) + """The gateway associated with this configuration. This is only meaningful + if "addresses" is also set. + + Setting the gateway causes NetworkManager to configure a standard + default route with the gateway as next hop. This is ignored if + "never-default" is set. An alternative is to configure the default + route explicitly with a manual route and /0 as prefix length. + + Note that the gateway usually conflicts with routing that + NetworkManager configures for WireGuard interfaces, so usually it + should not be set in that case. See "ip4-auto-default-route".""" ignore_auto_dns: Optional[bool] = field( - metadata={'dbus_name': 'ignore-auto-dns', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'ignore-auto-dns', + 'dbus_type': 'b', + }, + default=None, ) + """When "method" is set to "auto" and this property to TRUE, automatically + configured name servers and search domains are ignored and only name + servers and search domains specified in the "dns" and "dns-search" + properties, if any, are used.""" ignore_auto_routes: Optional[bool] = field( - metadata={'dbus_name': 'ignore-auto-routes', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'ignore-auto-routes', + 'dbus_type': 'b', + }, + default=None, ) + """When "method" is set to "auto" and this property to TRUE, automatically + configured routes are ignored and only routes specified in the + "routes" property, if any, are used.""" ip6_privacy: Optional[int] = field( - metadata={'dbus_name': 'ip6-privacy', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'ip6-privacy', + 'dbus_type': 'i', + }, default=None, ) + """Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If + enabled, it makes the kernel generate a temporary IPv6 address in + addition to the public one generated from MAC address via modified + EUI-64. This enhances privacy, but could cause problems in some + applications, on the other hand. The permitted values are: -1: + unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled + (prefer temporary addresses). + + Having a per-connection setting set to "-1" (unknown) means fallback + to global configuration "ipv6.ip6-privacy". + + If also global configuration is unspecified or set to "-1", fallback + to read "/proc/sys/net/ipv6/conf/default/use_tempaddr". + + Note that this setting is distinct from the Stable Privacy addresses + that can be enabled with the "addr-gen-mode" property's "stable- + privacy" setting as another way of avoiding host tracking with IPv6 + addresses.""" may_fail: Optional[bool] = field( - metadata={'dbus_name': 'may-fail', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'may-fail', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, allow overall network configuration to proceed even if the + configuration specified by this property times out. Note that at + least one IP configuration must succeed or overall network + configuration will still fail. For example, in IPv6-only networks, + setting this property to TRUE on the NMSettingIP4Config allows the + overall network configuration to succeed if IPv4 configuration fails + but IPv6 configuration completes successfully.""" method: Optional[str] = field( - metadata={'dbus_name': 'method', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'method', + 'dbus_type': 's', + }, + default=None, + ) + """IP configuration method. + + NMSettingIP4Config and NMSettingIP6Config both support "disabled", + "auto", "manual", and "link-local". See the subclass-specific + documentation for other values. + + In general, for the "auto" method, properties such as "dns" and + "routes" specify information that is added on to the information + returned from automatic configuration. The "ignore-auto-routes" and + "ignore-auto-dns" properties modify this behavior. + + For methods that imply no upstream network, such as "shared" or + "link-local", these properties must be empty. + + For IPv4 method "shared", the IP subnet can be configured by adding + one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note + that the shared method must be configured on the interface which + shares the internet to a subnet, not on the uplink which is shared.""" + mtu: Optional[int] = field( + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """Maximum transmission unit size, in bytes. If zero (the default), the MTU + is set automatically from router advertisements or is left equal to + the link-layer MTU. If greater than the link-layer MTU, or greater + than zero but less than the minimum IPv6 MTU of 1280, this value has + no effect.""" never_default: Optional[bool] = field( - metadata={'dbus_name': 'never-default', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'never-default', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, this connection will never be the default connection for this + IP type, meaning it will never be assigned the default route by + NetworkManager.""" ra_timeout: Optional[int] = field( - metadata={'dbus_name': 'ra-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'ra-timeout', + 'dbus_type': 'i', + }, default=None, ) + """A timeout for waiting Router Advertisements in seconds. If zero (the + default), a globally configured default is used. If still + unspecified, the timeout depends on the sysctl settings of the + device. + + Set to 2147483647 (MAXINT32) for infinity.""" required_timeout: Optional[int] = field( - metadata={'dbus_name': 'required-timeout', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'required-timeout', + 'dbus_type': 'i', + }, default=None, ) + """The minimum time interval in milliseconds for which dynamic IP + configuration should be tried before the connection succeeds. + + This property is useful for example if both IPv4 and IPv6 are + enabled and are allowed to fail. Normally the connection succeeds as + soon as one of the two address families completes; by setting a + required timeout for e.g. IPv4, one can ensure that even if IP6 + succeeds earlier than IPv4, NetworkManager waits some time for IPv4 + before the connection becomes active. + + Note that if "may-fail" is FALSE for the same address family, this + property has no effect as NetworkManager needs to wait for the full + DHCP timeout. + + A zero value means that no required timeout is present, -1 means the + default value (either configuration ipvx.required-timeout override + or zero).""" route_data: Optional[List[RouteData]] = field( - metadata={'dbus_name': 'route-data', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': RouteData}, + metadata={ + 'dbus_name': 'route-data', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': RouteData, + }, default=None, ) + """Array of IPv6 routes. Each route dictionary contains at least 'dest' and + 'prefix' entries, containing the destination IP address as a string, + and the prefix length as a uint32. Most routes will also have a + 'next-hop' entry, containing the next hop IP address as a string. If + the route has a 'metric' entry (containing a uint32), that will be + used as the metric for the route (otherwise NM will pick a default + value appropriate to the device). Additional attributes may also + exist on some routes.""" route_metric: Optional[int] = field( - metadata={'dbus_name': 'route-metric', 'dbus_type': 'x'}, + metadata={ + 'dbus_name': 'route-metric', + 'dbus_type': 'x', + }, default=None, ) + """The default metric for routes that don't explicitly specify a metric. + The default value -1 means that the metric is chosen automatically + based on the device type. The metric applies to dynamic routes, + manual (static) routes that don't have an explicit metric setting, + address prefix routes, and the default route. Note that for IPv6, + the kernel accepts zero (0) but coerces it to 1024 (user default). + Hence, setting this property to zero effectively mean setting it to + 1024. For IPv4, zero is a regular value for the metric.""" route_table: Optional[int] = field( - metadata={'dbus_name': 'route-table', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'route-table', + 'dbus_type': 'u', + }, + default=None, + ) + """Enable policy routing (source routing) and set the routing table used + when adding routes. + + This affects all routes, including device-routes, IPv4LL, DHCP, + SLAAC, default-routes and static routes. But note that static routes + can individually overwrite the setting by explicitly specifying a + non-zero routing table. + + If the table setting is left at zero, it is eligible to be + overwritten via global configuration. If the property is zero even + after applying the global configuration value, policy routing is + disabled for the address family of this connection. + + Policy routing disabled means that NetworkManager will add all + routes to the main table (except static routes that explicitly + configure a different table). Additionally, NetworkManager will not + delete any extraneous routes from tables except the main table. This + is to preserve backward compatibility for users who manage routing + tables outside of NetworkManager.""" + routing_rules: Optional[List[RoutingRules]] = field( + metadata={ + 'dbus_name': 'routing-rules', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': RoutingRules, + }, default=None, ) + """Array of dictionaries for routing rules. Each routing rule supports the + following options: action (y), dport-end (q), dport-start (q), + family (i), from (s), from-len (y), fwmark (u), fwmask (u), iifname + (s), invert (b), ipproto (s), oifname (s), priority (u), sport-end + (q), sport-start (q), supress-prefixlength (i), table (u), to (s), + tos (y), to-len (y), range-end (u), range-start (u).""" token: Optional[str] = field( - metadata={'dbus_name': 'token', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'token', + 'dbus_type': 's', + }, default=None, ) + """Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 + IPv6 tokenized interface identifiers. Useful with eui64 addr-gen- + mode.""" diff --git a/sdbus_async/networkmanager/settings/lowpan.py b/sdbus_async/networkmanager/settings/lowpan.py index 0d8bc9f..ae988ad 100644 --- a/sdbus_async/networkmanager/settings/lowpan.py +++ b/sdbus_async/networkmanager/settings/lowpan.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,6 +12,11 @@ class LowpanSettings(NetworkManagerSettingsMixin): """6LoWPAN Settings""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name or parent connection UUID + from which this 6LowPAN interface should be created.""" diff --git a/sdbus_async/networkmanager/settings/macsec.py b/sdbus_async/networkmanager/settings/macsec.py index 022dda2..f0dad7c 100644 --- a/sdbus_async/networkmanager/settings/macsec.py +++ b/sdbus_async/networkmanager/settings/macsec.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -10,40 +10,86 @@ @dataclass class MacsecSettings(NetworkManagerSettingsMixin): """MACSec Settings""" + secret_fields_names = ['mka_cak'] + secret_name = 'macsec' encrypt: Optional[bool] = field( - metadata={'dbus_name': 'encrypt', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'encrypt', + 'dbus_type': 'b', + }, + default=None, ) + """Whether the transmitted traffic must be encrypted.""" mka_cak: Optional[str] = field( - metadata={'dbus_name': 'mka-cak', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'mka-cak', + 'dbus_type': 's', + }, default=None, ) + """The pre-shared CAK (Connectivity Association Key) for MACsec Key + Agreement.""" mka_cak_flags: Optional[int] = field( - metadata={'dbus_name': 'mka-cak-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'mka-cak-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "mka-cak" property.""" mka_ckn: Optional[str] = field( - metadata={'dbus_name': 'mka-ckn', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'mka-ckn', + 'dbus_type': 's', + }, default=None, ) + """The pre-shared CKN (Connectivity-association Key Name) for MACsec Key + Agreement.""" mode: Optional[int] = field( - metadata={'dbus_name': 'mode', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'mode', + 'dbus_type': 'i', + }, default=None, ) + """Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key + Agreement) is obtained.""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name or parent connection UUID + from which this MACSEC interface should be created. If this + property is not specified, the connection must contain an + "802-3-ethernet" setting with a "mac-address" property.""" port: Optional[int] = field( - metadata={'dbus_name': 'port', 'dbus_type': 'i'}, - default=1, + metadata={ + 'dbus_name': 'port', + 'dbus_type': 'i', + }, + default=None, ) + """The port component of the SCI (Secure Channel Identifier), between 1 and + 65534.""" send_sci: Optional[bool] = field( - metadata={'dbus_name': 'send-sci', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'send-sci', + 'dbus_type': 'b', + }, + default=None, ) + """Specifies whether the SCI (Secure Channel Identifier) is included in + every packet.""" validation: Optional[int] = field( - metadata={'dbus_name': 'validation', 'dbus_type': 'i'}, - default=2, + metadata={ + 'dbus_name': 'validation', + 'dbus_type': 'i', + }, + default=None, ) + """Specifies the validation mode for incoming frames.""" diff --git a/sdbus_async/networkmanager/settings/macvlan.py b/sdbus_async/networkmanager/settings/macvlan.py index f97fb9c..bd5017f 100644 --- a/sdbus_async/networkmanager/settings/macvlan.py +++ b/sdbus_async/networkmanager/settings/macvlan.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,18 +12,38 @@ class MacvlanSettings(NetworkManagerSettingsMixin): """MAC VLAN Settings""" mode: Optional[int] = field( - metadata={'dbus_name': 'mode', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mode', + 'dbus_type': 'u', + }, default=None, ) + """The macvlan mode, which specifies the communication mechanism between + multiple macvlans on the same lower device.""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name or parent connection UUID + from which this MAC-VLAN interface should be created. If this + property is not specified, the connection must contain an + "802-3-ethernet" setting with a "mac-address" property.""" promiscuous: Optional[bool] = field( - metadata={'dbus_name': 'promiscuous', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'promiscuous', + 'dbus_type': 'b', + }, + default=None, ) + """Whether the interface should be put in promiscuous mode.""" tap: Optional[bool] = field( - metadata={'dbus_name': 'tap', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'tap', + 'dbus_type': 'b', + }, + default=None, ) + """Whether the interface should be a MACVTAP.""" diff --git a/sdbus_async/networkmanager/settings/match.py b/sdbus_async/networkmanager/settings/match.py index 033d73f..048dc6e 100644 --- a/sdbus_async/networkmanager/settings/match.py +++ b/sdbus_async/networkmanager/settings/match.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,18 +12,83 @@ class MatchSettings(NetworkManagerSettingsMixin): """Match settings""" driver: Optional[List[str]] = field( - metadata={'dbus_name': 'driver', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'driver', + 'dbus_type': 'as', + }, default=None, ) + """A list of driver names to match. Each element is a shell wildcard + pattern. + + See NMSettingMatch:interface-name for how special characters '|', + '&', '!' and '\\' are used for optional and mandatory matches and + inverting the pattern.""" interface_name: Optional[List[str]] = field( - metadata={'dbus_name': 'interface-name', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'interface-name', + 'dbus_type': 'as', + }, default=None, ) + """A list of interface names to match. Each element is a shell wildcard + pattern. + + An element can be prefixed with a pipe symbol (|) or an ampersand + (&). The former means that the element is optional and the latter + means that it is mandatory. If there are any optional elements, than + the match evaluates to true if at least one of the optional element + matches (logical OR). If there are any mandatory elements, then they + all must match (logical AND). By default, an element is optional. + This means that an element "foo" behaves the same as "|foo". An + element can also be inverted with exclamation mark (!) between the + pipe symbol (or the ampersand) and before the pattern. Note that + "!foo" is a shortcut for the mandatory match "&!foo". Finally, a + backslash can be used at the beginning of the element (after the + optional special characters) to escape the start of the pattern. For + example, "&\\!a" is an mandatory match for literally "!a".""" kernel_command_line: Optional[List[str]] = field( - metadata={'dbus_name': 'kernel-command-line', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'kernel-command-line', + 'dbus_type': 'as', + }, default=None, ) + """A list of kernel command line arguments to match. This may be used to + check whether a specific kernel command line option is set (or + unset, if prefixed with the exclamation mark). The argument must + either be a single word, or an assignment (i.e. two words, joined by + "="). In the former case the kernel command line is searched for the + word appearing as is, or as left hand side of an assignment. In the + latter case, the exact assignment is looked for with right and left + hand side matching. Wildcard patterns are not supported. + + See NMSettingMatch:interface-name for how special characters '|', + '&', '!' and '\\' are used for optional and mandatory matches and + inverting the match.""" path: Optional[List[str]] = field( - metadata={'dbus_name': 'path', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'path', + 'dbus_type': 'as', + }, default=None, ) + """A list of paths to match against the ID_PATH udev property of devices. + ID_PATH represents the topological persistent path of a device. It + typically contains a subsystem string (pci, usb, platform, etc.) and + a subsystem-specific identifier. + + For PCI devices the path has the form + "pci-$domain:$bus:$device.$function", where each variable is an + hexadecimal value; for example "pci-0000:0a:00.0". + + The path of a device can be obtained with "udevadm info + /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" + property exported by NetworkManager ("nmcli -f general.path device + show $dev"). + + Each element of the list is a shell wildcard pattern. + + See NMSettingMatch:interface-name for how special characters '|', + '&', '!' and '\\' are used for optional and mandatory matches and + inverting the pattern.""" diff --git a/sdbus_async/networkmanager/settings/olpc_mesh.py b/sdbus_async/networkmanager/settings/olpc_mesh.py index dc6cf5b..f4e149c 100644 --- a/sdbus_async/networkmanager/settings/olpc_mesh.py +++ b/sdbus_async/networkmanager/settings/olpc_mesh.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,14 +12,30 @@ class OlpcMeshSettings(NetworkManagerSettingsMixin): """OLPC Wireless Mesh Settings""" channel: Optional[int] = field( - metadata={'dbus_name': 'channel', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'channel', + 'dbus_type': 'u', + }, default=None, ) + """Channel on which the mesh network to join is located.""" dhcp_anycast_address: Optional[bytes] = field( - metadata={'dbus_name': 'dhcp-anycast-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'dhcp-anycast-address', + 'dbus_type': 'ay', + }, default=None, ) + """Anycast DHCP MAC address used when requesting an IP address via DHCP. + The specific anycast address used determines which DHCP server class + answers the request. + + This is currently only implemented by dhclient DHCP plugin.""" ssid: Optional[bytes] = field( - metadata={'dbus_name': 'ssid', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'ssid', + 'dbus_type': 'ay', + }, default=None, ) + """SSID of the mesh network to join.""" diff --git a/sdbus_async/networkmanager/settings/ovs_bridge.py b/sdbus_async/networkmanager/settings/ovs_bridge.py index 428a1f6..7167450 100644 --- a/sdbus_async/networkmanager/settings/ovs_bridge.py +++ b/sdbus_async/networkmanager/settings/ovs_bridge.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,22 +12,42 @@ class OvsBridgeSettings(NetworkManagerSettingsMixin): """OvsBridge Link Settings""" datapath_type: Optional[str] = field( - metadata={'dbus_name': 'datapath-type', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'datapath-type', + 'dbus_type': 's', + }, default=None, ) + """The data path type. One of "system", "netdev" or empty.""" fail_mode: Optional[str] = field( - metadata={'dbus_name': 'fail-mode', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'fail-mode', + 'dbus_type': 's', + }, default=None, ) + """The bridge failure mode. One of "secure", "standalone" or empty.""" mcast_snooping_enable: Optional[bool] = field( - metadata={'dbus_name': 'mcast-snooping-enable', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'mcast-snooping-enable', + 'dbus_type': 'b', + }, + default=None, ) + """Enable or disable multicast snooping.""" rstp_enable: Optional[bool] = field( - metadata={'dbus_name': 'rstp-enable', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'rstp-enable', + 'dbus_type': 'b', + }, + default=None, ) + """Enable or disable RSTP.""" stp_enable: Optional[bool] = field( - metadata={'dbus_name': 'stp-enable', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'stp-enable', + 'dbus_type': 'b', + }, + default=None, ) + """Enable or disable STP.""" diff --git a/sdbus_async/networkmanager/settings/ovs_dpdk.py b/sdbus_async/networkmanager/settings/ovs_dpdk.py index 5e2fab9..b078909 100644 --- a/sdbus_async/networkmanager/settings/ovs_dpdk.py +++ b/sdbus_async/networkmanager/settings/ovs_dpdk.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,10 +12,20 @@ class OvsDpdkSettings(NetworkManagerSettingsMixin): """OvsDpdk Link Settings""" devargs: Optional[str] = field( - metadata={'dbus_name': 'devargs', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'devargs', + 'dbus_type': 's', + }, default=None, ) + """Open vSwitch DPDK device arguments.""" n_rxq: Optional[int] = field( - metadata={'dbus_name': 'n-rxq', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'n-rxq', + 'dbus_type': 'u', + }, default=None, ) + """Open vSwitch DPDK number of rx queues. Defaults to zero which means to + leave the parameter in OVS unspecified and effectively configures + one queue.""" diff --git a/sdbus_async/networkmanager/settings/ovs_external_ids.py b/sdbus_async/networkmanager/settings/ovs_external_ids.py index 304b4b1..de28638 100644 --- a/sdbus_async/networkmanager/settings/ovs_external_ids.py +++ b/sdbus_async/networkmanager/settings/ovs_external_ids.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,6 +12,10 @@ class OvsExternalIdsSettings(NetworkManagerSettingsMixin): """OVS External IDs Settings""" data: Optional[Dict[str, str]] = field( - metadata={'dbus_name': 'data', 'dbus_type': 'a{ss}'}, + metadata={ + 'dbus_name': 'data', + 'dbus_type': 'a{ss}', + }, default=None, ) + """A dictionary of key/value pairs with exernal-ids for OVS.""" diff --git a/sdbus_async/networkmanager/settings/ovs_interface.py b/sdbus_async/networkmanager/settings/ovs_interface.py index 4c38428..dbe4f29 100644 --- a/sdbus_async/networkmanager/settings/ovs_interface.py +++ b/sdbus_async/networkmanager/settings/ovs_interface.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -11,7 +11,24 @@ class OvsInterfaceSettings(NetworkManagerSettingsMixin): """Open vSwitch Interface Settings""" + ofport_request: Optional[int] = field( + metadata={ + 'dbus_name': 'ofport-request', + 'dbus_type': 'u', + }, + default=None, + ) + """Open vSwitch openflow port number. Defaults to zero which means that + port number will not be specified and it will be chosen randomly by + ovs. OpenFlow ports are the network interfaces for passing packets + between OpenFlow processing and the rest of the network. OpenFlow + switches connect logically to each other via their OpenFlow ports.""" ovs_interface_type: Optional[str] = field( - metadata={'dbus_name': 'type', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'type', + 'dbus_type': 's', + }, default=None, ) + """The interface type. Either "internal", "system", "patch", "dpdk", or + empty.""" diff --git a/sdbus_async/networkmanager/settings/ovs_patch.py b/sdbus_async/networkmanager/settings/ovs_patch.py index f01f61c..45cb376 100644 --- a/sdbus_async/networkmanager/settings/ovs_patch.py +++ b/sdbus_async/networkmanager/settings/ovs_patch.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,6 +12,11 @@ class OvsPatchSettings(NetworkManagerSettingsMixin): """OvsPatch Link Settings""" peer: Optional[str] = field( - metadata={'dbus_name': 'peer', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'peer', + 'dbus_type': 's', + }, default=None, ) + """Specifies the name of the interface for the other side of the patch. The + patch on the other side must also set this interface as peer.""" diff --git a/sdbus_async/networkmanager/settings/ovs_port.py b/sdbus_async/networkmanager/settings/ovs_port.py index 13c4b05..898fd31 100644 --- a/sdbus_async/networkmanager/settings/ovs_port.py +++ b/sdbus_async/networkmanager/settings/ovs_port.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,26 +12,51 @@ class OvsPortSettings(NetworkManagerSettingsMixin): """OvsPort Link Settings""" bond_downdelay: Optional[int] = field( - metadata={'dbus_name': 'bond-downdelay', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'bond-downdelay', + 'dbus_type': 'u', + }, default=None, ) + """The time port must be inactive in order to be considered down.""" bond_mode: Optional[str] = field( - metadata={'dbus_name': 'bond-mode', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'bond-mode', + 'dbus_type': 's', + }, default=None, ) + """Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp".""" bond_updelay: Optional[int] = field( - metadata={'dbus_name': 'bond-updelay', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'bond-updelay', + 'dbus_type': 'u', + }, default=None, ) + """The time port must be active before it starts forwarding traffic.""" lacp: Optional[str] = field( - metadata={'dbus_name': 'lacp', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'lacp', + 'dbus_type': 's', + }, default=None, ) + """LACP mode. One of "active", "off", or "passive".""" tag: Optional[int] = field( - metadata={'dbus_name': 'tag', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'tag', + 'dbus_type': 'u', + }, default=None, ) + """The VLAN tag in the range 0-4095.""" vlan_mode: Optional[str] = field( - metadata={'dbus_name': 'vlan-mode', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'vlan-mode', + 'dbus_type': 's', + }, default=None, ) + """The VLAN mode. One of "access", "native-tagged", "native-untagged", + "trunk" or unset.""" diff --git a/sdbus_async/networkmanager/settings/ppp.py b/sdbus_async/networkmanager/settings/ppp.py index dcb15d1..ab79f66 100644 --- a/sdbus_async/networkmanager/settings/ppp.py +++ b/sdbus_async/networkmanager/settings/ppp.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,74 +12,168 @@ class PppSettings(NetworkManagerSettingsMixin): """Point-to-Point Protocol Settings""" baud: Optional[int] = field( - metadata={'dbus_name': 'baud', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'baud', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, instruct pppd to set the serial port to the specified + baudrate. This value should normally be left as 0 to automatically + choose the speed.""" crtscts: Optional[bool] = field( - metadata={'dbus_name': 'crtscts', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'crtscts', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, specify that pppd should set the serial port to use hardware + flow control with RTS and CTS signals. This value should normally + be set to FALSE.""" lcp_echo_failure: Optional[int] = field( - metadata={'dbus_name': 'lcp-echo-failure', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'lcp-echo-failure', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, instruct pppd to presume the connection to the peer has + failed if the specified number of LCP echo-requests go unanswered by + the peer. The "lcp-echo-interval" property must also be set to a + non-zero value if this property is used.""" lcp_echo_interval: Optional[int] = field( - metadata={'dbus_name': 'lcp-echo-interval', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'lcp-echo-interval', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, instruct pppd to send an LCP echo-request frame to the peer + every n seconds (where n is the specified value). Note that some + PPP peers will respond to echo requests and some will not, and it is + not possible to autodetect this.""" mppe_stateful: Optional[bool] = field( - metadata={'dbus_name': 'mppe-stateful', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'mppe-stateful', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, stateful MPPE is used. See pppd documentation for more + information on stateful MPPE.""" mru: Optional[int] = field( - metadata={'dbus_name': 'mru', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mru', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, instruct pppd to request that the peer send packets no + larger than the specified size. If non-zero, the MRU should be + between 128 and 16384.""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, instruct pppd to send packets no larger than the specified + size.""" no_vj_comp: Optional[bool] = field( - metadata={'dbus_name': 'no-vj-comp', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'no-vj-comp', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, Van Jacobsen TCP header compression will not be requested.""" noauth: Optional[bool] = field( - metadata={'dbus_name': 'noauth', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'noauth', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, do not require the other side (usually the PPP server) to + authenticate itself to the client. If FALSE, require authentication + from the remote side. In almost all cases, this should be TRUE.""" nobsdcomp: Optional[bool] = field( - metadata={'dbus_name': 'nobsdcomp', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'nobsdcomp', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, BSD compression will not be requested.""" nodeflate: Optional[bool] = field( - metadata={'dbus_name': 'nodeflate', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'nodeflate', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, "deflate" compression will not be requested.""" refuse_chap: Optional[bool] = field( - metadata={'dbus_name': 'refuse-chap', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'refuse-chap', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, the CHAP authentication method will not be used.""" refuse_eap: Optional[bool] = field( - metadata={'dbus_name': 'refuse-eap', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'refuse-eap', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, the EAP authentication method will not be used.""" refuse_mschap: Optional[bool] = field( - metadata={'dbus_name': 'refuse-mschap', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'refuse-mschap', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, the MSCHAP authentication method will not be used.""" refuse_mschapv2: Optional[bool] = field( - metadata={'dbus_name': 'refuse-mschapv2', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'refuse-mschapv2', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, the MSCHAPv2 authentication method will not be used.""" refuse_pap: Optional[bool] = field( - metadata={'dbus_name': 'refuse-pap', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'refuse-pap', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, the PAP authentication method will not be used.""" require_mppe: Optional[bool] = field( - metadata={'dbus_name': 'require-mppe', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'require-mppe', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for + the PPP session. If either 64-bit or 128-bit MPPE is not available + the session will fail. Note that MPPE is not used on mobile + broadband connections.""" require_mppe_128: Optional[bool] = field( - metadata={'dbus_name': 'require-mppe-128', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'require-mppe-128', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be + required for the PPP session, and the "require-mppe" property must + also be set to TRUE. If 128-bit MPPE is not available the session + will fail.""" diff --git a/sdbus_async/networkmanager/settings/pppoe.py b/sdbus_async/networkmanager/settings/pppoe.py index e2cbb42..826b7a9 100644 --- a/sdbus_async/networkmanager/settings/pppoe.py +++ b/sdbus_async/networkmanager/settings/pppoe.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -10,24 +10,53 @@ @dataclass class PppoeSettings(NetworkManagerSettingsMixin): """PPP-over-Ethernet Settings""" + secret_fields_names = ['password'] + secret_name = 'pppoe' parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name on which this PPPoE + connection should be created. If this property is not specified, + the connection is activated on the interface specified in + "interface-name" of NMSettingConnection.""" password: Optional[str] = field( - metadata={'dbus_name': 'password', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'password', + 'dbus_type': 's', + }, default=None, ) + """Password used to authenticate with the PPPoE service.""" password_flags: Optional[int] = field( - metadata={'dbus_name': 'password-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'password-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "password" property.""" service: Optional[str] = field( - metadata={'dbus_name': 'service', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'service', + 'dbus_type': 's', + }, default=None, ) + """If specified, instruct PPPoE to only initiate sessions with access + concentrators that provide the specified service. For most + providers, this should be left blank. It is only required if there + are multiple access concentrators or a specific service is known to + be required.""" username: Optional[str] = field( - metadata={'dbus_name': 'username', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'username', + 'dbus_type': 's', + }, default=None, ) + """Username used to authenticate with the PPPoE service.""" diff --git a/sdbus_async/networkmanager/settings/profile.py b/sdbus_async/networkmanager/settings/profile.py index d8bf643..d997fa6 100644 --- a/sdbus_async/networkmanager/settings/profile.py +++ b/sdbus_async/networkmanager/settings/profile.py @@ -1,12 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field, fields -from typing import Any, Dict, Optional +from typing import Any, Dict, Generator, Optional from .base import NetworkManagerSettingsMixin -from .connection import ConnectionSettings -from .ipv4 import Ipv4Settings -from .ipv6 import Ipv6Settings from .adsl import AdslSettings from .bluetooth import BluetoothSettings from .bond import BondSettings @@ -14,13 +14,19 @@ from .bridge import BridgeSettings from .bridge_port import BridgePortSettings from .cdma import CdmaSettings +from .connection import ConnectionSettings from .dcb import DcbSettings +from .dummy import DummySettings +from .eapol import EapolSettings from .ethernet import EthernetSettings +from .ethtool import EthtoolSettings +from .generic import GenericSettings from .gsm import GsmSettings from .hostname import HostnameSettings -from .ieee802_1x import Ieee8021XSettings from .infiniband import InfinibandSettings from .ip_tunnel import IpTunnelSettings +from .ipv4 import Ipv4Settings +from .ipv6 import Ipv6Settings from .lowpan import LowpanSettings from .macsec import MacsecSettings from .macvlan import MacvlanSettings @@ -36,6 +42,8 @@ from .pppoe import PppoeSettings from .proxy import ProxySettings from .serial import SerialSettings +from .sriov import SriovSettings +from .tc import TcSettings from .team import TeamSettings from .team_port import TeamPortSettings from .tun import TunSettings @@ -88,28 +96,12 @@ class ConnectionProfile: A group of related key/value pairs describing a specific piece of a Connection (profile). Keys are also referred to as properties. """ - # The settings object "connection" is special: It must always be present - # because it contains the UUID and the connection type which are required: - connection: ConnectionSettings = field( - metadata={'dbus_name': 'connection', - 'settings_class': ConnectionSettings}, - ) - # The list of the remaining settings classes was generated by - # tools/generate-settings-dataclasses.py which generates the - # settings classes themselfes as well. + # The list of the settings classes was generated by + # tools/generate-settings-dataclasses-jinja.py which generates the + # settings classes themselves as well. # If possible, please make changes by also updating the script. # start of the generated list of settings classes - ipv4: Optional[Ipv4Settings] = field( - metadata={'dbus_name': 'ipv4', - 'settings_class': Ipv4Settings}, - default=None, - ) - ipv6: Optional[Ipv6Settings] = field( - metadata={'dbus_name': 'ipv6', - 'settings_class': Ipv6Settings}, - default=None, - ) adsl: Optional[AdslSettings] = field( metadata={'dbus_name': 'adsl', 'settings_class': AdslSettings}, @@ -145,16 +137,41 @@ class ConnectionProfile: 'settings_class': CdmaSettings}, default=None, ) + connection: ConnectionSettings = field( + metadata={'dbus_name': 'connection', + 'settings_class': ConnectionSettings}, + default_factory=ConnectionSettings, + ) dcb: Optional[DcbSettings] = field( metadata={'dbus_name': 'dcb', 'settings_class': DcbSettings}, default=None, ) + dummy: Optional[DummySettings] = field( + metadata={'dbus_name': 'dummy', + 'settings_class': DummySettings}, + default=None, + ) + eapol: Optional[EapolSettings] = field( + metadata={'dbus_name': '802-1x', + 'settings_class': EapolSettings}, + default=None, + ) ethernet: Optional[EthernetSettings] = field( metadata={'dbus_name': '802-3-ethernet', 'settings_class': EthernetSettings}, default=None, ) + ethtool: Optional[EthtoolSettings] = field( + metadata={'dbus_name': 'ethtool', + 'settings_class': EthtoolSettings}, + default=None, + ) + generic: Optional[GenericSettings] = field( + metadata={'dbus_name': 'generic', + 'settings_class': GenericSettings}, + default=None, + ) gsm: Optional[GsmSettings] = field( metadata={'dbus_name': 'gsm', 'settings_class': GsmSettings}, @@ -165,11 +182,6 @@ class ConnectionProfile: 'settings_class': HostnameSettings}, default=None, ) - ieee802_1x: Optional[Ieee8021XSettings] = field( - metadata={'dbus_name': '802-1x', - 'settings_class': Ieee8021XSettings}, - default=None, - ) infiniband: Optional[InfinibandSettings] = field( metadata={'dbus_name': 'infiniband', 'settings_class': InfinibandSettings}, @@ -180,6 +192,16 @@ class ConnectionProfile: 'settings_class': IpTunnelSettings}, default=None, ) + ipv4: Optional[Ipv4Settings] = field( + metadata={'dbus_name': 'ipv4', + 'settings_class': Ipv4Settings}, + default=None, + ) + ipv6: Optional[Ipv6Settings] = field( + metadata={'dbus_name': 'ipv6', + 'settings_class': Ipv6Settings}, + default=None, + ) lowpan: Optional[LowpanSettings] = field( metadata={'dbus_name': '6lowpan', 'settings_class': LowpanSettings}, @@ -255,6 +277,16 @@ class ConnectionProfile: 'settings_class': SerialSettings}, default=None, ) + sriov: Optional[SriovSettings] = field( + metadata={'dbus_name': 'sriov', + 'settings_class': SriovSettings}, + default=None, + ) + tc: Optional[TcSettings] = field( + metadata={'dbus_name': 'tc', + 'settings_class': TcSettings}, + default=None, + ) team: Optional[TeamSettings] = field( metadata={'dbus_name': 'team', 'settings_class': TeamSettings}, @@ -348,13 +380,13 @@ def to_settings_dict(self, defaults: bool = False) -> SettingsDict: """Return a simple dictionary using the same key names like the dbus dict from to_dbus(), but without the dbus signatures returned by it. - Contrary to dataclasses.asdict(), it provides the orignal dbus keys, + Contrary to dataclasses.asdict(), it provides the original dbus keys, e.g. with numerical prefixes like "802-11-", dashes, and "id"/"type". The key names provided are exactly as documented in these tables: https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html - param defaults: Whether properies with default values are returned. + param defaults: Whether properties with default values are returned. """ new_dict = {} for x in fields(self): @@ -378,13 +410,16 @@ def from_dbus(cls, dbus_dict: NetworkManagerConnectionProperties if group: for key in ("addresses", "routes"): group.pop(key, None) - try: - unvarianted_options: Dict[str, Any] = { - SETTING_DBUS_NAME_TO_NAME[k]: SETTING_TO_CLASS[k].from_dbus(v) - for k, v in dbus_dict.items()} - except KeyError as e: - print(dbus_dict) - raise e + + unvarianted_options: Dict[str, Any] = {} + for k, v in dbus_dict.items(): + try: + unvarianted_options[SETTING_DBUS_NAME_TO_NAME[k]] = ( + SETTING_TO_CLASS[k].from_dbus(v) + ) + except KeyError: + ... + return cls(**unvarianted_options) @classmethod @@ -403,6 +438,61 @@ def from_settings_dict( for k, v in settings_dict.items()} return cls(**unvarianted_options) + def update(self, other: ConnectionProfile) -> None: + """Update this connection profile with the settings from the other. + + Similar to dict.update method. + """ + for f in fields(other): + settings_field_name = f.name + other_settings = getattr(other, settings_field_name) + + if other_settings is None: + continue + + my_settings = getattr(self, settings_field_name) + + if my_settings is None: + setattr(self, settings_field_name, other_settings) + continue + + for setting_field in fields(other_settings): + setting_field_name = setting_field.name + + other_setting = getattr(other_settings, setting_field_name) + + if other_setting is None: + continue + + setattr(my_settings, setting_field_name, other_setting) + + def update_secrets_generator( + self) -> Generator[str, ConnectionProfile, None]: + for attr_name, value in vars(self).items(): + if value is None: + continue + + if not isinstance(value, NetworkManagerSettingsMixin): + continue + + secret_setting_name = value.secret_name + + if not secret_setting_name: + continue + + secret_profile = yield secret_setting_name + current_setting_secrets = getattr(secret_profile, attr_name) + + for secret_attribute in value.secret_fields_names: + setattr( + value, + secret_attribute, + getattr( + current_setting_secrets, + secret_attribute, + ), + ) + SETTING_DBUS_NAME_TO_NAME: Dict[str, str] = { f.metadata['dbus_name']: f.name diff --git a/sdbus_async/networkmanager/settings/proxy.py b/sdbus_async/networkmanager/settings/proxy.py index 5aac8c8..6cf2c14 100644 --- a/sdbus_async/networkmanager/settings/proxy.py +++ b/sdbus_async/networkmanager/settings/proxy.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,18 +12,36 @@ class ProxySettings(NetworkManagerSettingsMixin): """WWW Proxy Settings""" browser_only: Optional[bool] = field( - metadata={'dbus_name': 'browser-only', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'browser-only', + 'dbus_type': 'b', + }, + default=None, ) + """Whether the proxy configuration is for browser only.""" method: Optional[int] = field( - metadata={'dbus_name': 'method', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'method', + 'dbus_type': 'i', + }, default=None, ) + """Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE + (0)""" pac_script: Optional[str] = field( - metadata={'dbus_name': 'pac-script', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'pac-script', + 'dbus_type': 's', + }, default=None, ) + """PAC script for the connection. This is an UTF-8 encoded javascript code + that defines a FindProxyForURL() function.""" pac_url: Optional[str] = field( - metadata={'dbus_name': 'pac-url', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'pac-url', + 'dbus_type': 's', + }, default=None, ) + """PAC URL for obtaining PAC file.""" diff --git a/sdbus_async/networkmanager/settings/serial.py b/sdbus_async/networkmanager/settings/serial.py index 5cb2cc1..7d07d8c 100644 --- a/sdbus_async/networkmanager/settings/serial.py +++ b/sdbus_async/networkmanager/settings/serial.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,22 +12,45 @@ class SerialSettings(NetworkManagerSettingsMixin): """Serial Link Settings""" baud: Optional[int] = field( - metadata={'dbus_name': 'baud', 'dbus_type': 'u'}, - default=57600, + metadata={ + 'dbus_name': 'baud', + 'dbus_type': 'u', + }, + default=None, ) + """Speed to use for communication over the serial port. Note that this + value usually has no effect for mobile broadband modems as they + generally ignore speed settings and use the highest available speed.""" bits: Optional[int] = field( - metadata={'dbus_name': 'bits', 'dbus_type': 'u'}, - default=8, + metadata={ + 'dbus_name': 'bits', + 'dbus_type': 'u', + }, + default=None, ) + """Byte-width of the serial communication. The 8 in "8n1" for example.""" parity: Optional[int] = field( - metadata={'dbus_name': 'parity', 'dbus_type': 'y'}, + metadata={ + 'dbus_name': 'parity', + 'dbus_type': 'y', + }, default=None, ) + """Parity setting of the serial port.""" send_delay: Optional[int] = field( - metadata={'dbus_name': 'send-delay', 'dbus_type': 't'}, + metadata={ + 'dbus_name': 'send-delay', + 'dbus_type': 't', + }, default=None, ) + """Time to delay between each byte sent to the modem, in microseconds.""" stopbits: Optional[int] = field( - metadata={'dbus_name': 'stopbits', 'dbus_type': 'u'}, - default=1, + metadata={ + 'dbus_name': 'stopbits', + 'dbus_type': 'u', + }, + default=None, ) + """Number of stop bits for communication on the serial port. Either 1 or + 2. The 1 in "8n1" for example.""" diff --git a/sdbus_async/networkmanager/settings/sriov.py b/sdbus_async/networkmanager/settings/sriov.py new file mode 100644 index 0000000..5ed5bc1 --- /dev/null +++ b/sdbus_async/networkmanager/settings/sriov.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Any, List, Optional, Tuple +from .base import NetworkManagerSettingsMixin +from .datatypes import Vfs + + +@dataclass +class SriovSettings(NetworkManagerSettingsMixin): + """SR-IOV settings""" + + autoprobe_drivers: Optional[int] = field( + metadata={ + 'dbus_name': 'autoprobe-drivers', + 'dbus_type': 'i', + }, + default=None, + ) + """Whether to autoprobe virtual functions by a compatible driver. + + If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a + compatible driver and if this succeeds a new network interface will + be instantiated for each VF. + + If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no + network interfaces will be created for them. + + When set to NM_TERNARY_DEFAULT (-1), the global default is used; in + case the global default is unspecified it is assumed to be + NM_TERNARY_TRUE (1).""" + total_vfs: Optional[int] = field( + metadata={ + 'dbus_name': 'total-vfs', + 'dbus_type': 'u', + }, + default=None, + ) + """The total number of virtual functions to create. + + Note that when the sriov setting is present NetworkManager enforces + the number of virtual functions on the interface (also when it is + zero) during activation and resets it upon deactivation. To prevent + any changes to SR-IOV parameters don't add a sriov setting to the + connection.""" + vfs: Optional[List[Vfs]] = field( + metadata={ + 'dbus_name': 'vfs', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': Vfs, + }, + default=None, + ) + """Array of virtual function descriptors. + + Each VF descriptor is a dictionary mapping attribute names to + GVariant values. The 'index' entry is mandatory for each VF. + + When represented as string a VF is in the form: + + "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". + + for example: + + "2 mac=00:11:22:33:44:55 spoof-check=true". + + Multiple VFs can be specified using a comma as separator. Currently, + the following attributes are supported: mac, spoof-check, trust, + min-tx-rate, max-tx-rate, vlans. + + The "vlans" attribute is represented as a semicolon-separated list + of VLAN descriptors, where each descriptor has the form + + "ID[.PRIORITY[.PROTO]]". + + PROTO can be either 'q' for 802.1Q (the default) or 'ad' for + 802.1ad.""" diff --git a/sdbus_async/networkmanager/settings/tc.py b/sdbus_async/networkmanager/settings/tc.py new file mode 100644 index 0000000..89c6a49 --- /dev/null +++ b/sdbus_async/networkmanager/settings/tc.py @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Any, List, Optional, Tuple +from .base import NetworkManagerSettingsMixin +from .datatypes import Qdiscs, Tfilters + + +@dataclass +class TcSettings(NetworkManagerSettingsMixin): + """Linux Traffic Control Settings""" + + qdiscs: Optional[List[Qdiscs]] = field( + metadata={ + 'dbus_name': 'qdiscs', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': Qdiscs, + }, + default=None, + ) + """Array of TC queueing disciplines. + + When the "tc" setting is present, qdiscs from this property are + applied upon activation. If the property is empty, all qdiscs are + removed and the device will only have the default qdisc assigned by + kernel according to the "net.core.default_qdisc" sysctl. + + If the "tc" setting is not present, NetworkManager doesn't touch the + qdiscs present on the interface.""" + tfilters: Optional[List[Tfilters]] = field( + metadata={ + 'dbus_name': 'tfilters', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': Tfilters, + }, + default=None, + ) + """Array of TC traffic filters. + + When the "tc" setting is present, filters from this property are + applied upon activation. If the property is empty, NetworkManager + removes all the filters. + + If the "tc" setting is not present, NetworkManager doesn't touch the + filters present on the interface.""" diff --git a/sdbus_async/networkmanager/settings/team.py b/sdbus_async/networkmanager/settings/team.py index 7a43390..023b025 100644 --- a/sdbus_async/networkmanager/settings/team.py +++ b/sdbus_async/networkmanager/settings/team.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin from .datatypes import LinkWatchers @@ -13,72 +13,155 @@ class TeamSettings(NetworkManagerSettingsMixin): """Teaming Settings""" config: Optional[str] = field( - metadata={'dbus_name': 'config', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'config', + 'dbus_type': 's', + }, default=None, ) + """The JSON configuration for the team network interface. The property + should contain raw JSON configuration data suitable for teamd, + because the value is passed directly to teamd. If not specified, the + default configuration is used. See man teamd.conf for the format + details.""" interface_name: Optional[str] = field( - metadata={'dbus_name': 'interface-name', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'interface-name', + 'dbus_type': 's', + }, default=None, ) + """Deprecated in favor of connection.interface-name, but can be used for + backward-compatibility with older daemons, to set the team's + interface name.""" link_watchers: Optional[List[LinkWatchers]] = field( - metadata={'dbus_name': 'link-watchers', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': LinkWatchers}, + metadata={ + 'dbus_name': 'link-watchers', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': LinkWatchers, + }, default=None, ) + """Link watchers configuration for the connection: each link watcher is + defined by a dictionary, whose keys depend upon the selected link + watcher. Available link watchers are 'ethtool', 'nsna_ping' and + 'arp_ping' and it is specified in the dictionary with the key + 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', + 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', + 'target-host'; arp_ping: all the ones in nsna_ping and 'source- + host', 'validate-active', 'validate-inactive', 'send-always'. See + teamd.conf man for more details.""" mcast_rejoin_count: Optional[int] = field( - metadata={'dbus_name': 'mcast-rejoin-count', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'mcast-rejoin-count', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd mcast_rejoin.count.""" mcast_rejoin_interval: Optional[int] = field( - metadata={'dbus_name': 'mcast-rejoin-interval', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'mcast-rejoin-interval', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd mcast_rejoin.interval.""" notify_peers_count: Optional[int] = field( - metadata={'dbus_name': 'notify-peers-count', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'notify-peers-count', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd notify_peers.count.""" notify_peers_interval: Optional[int] = field( - metadata={'dbus_name': 'notify-peers-interval', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'notify-peers-interval', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd notify_peers.interval.""" runner: Optional[str] = field( - metadata={'dbus_name': 'runner', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'runner', + 'dbus_type': 's', + }, default=None, ) + """Corresponds to the teamd runner.name. Permitted values are: + "roundrobin", "broadcast", "activebackup", "loadbalance", "lacp", + "random".""" runner_active: Optional[bool] = field( - metadata={'dbus_name': 'runner-active', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'runner-active', + 'dbus_type': 'b', + }, + default=None, ) + """Corresponds to the teamd runner.active.""" runner_agg_select_policy: Optional[str] = field( - metadata={'dbus_name': 'runner-agg-select-policy', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'runner-agg-select-policy', + 'dbus_type': 's', + }, default=None, ) + """Corresponds to the teamd runner.agg_select_policy.""" runner_fast_rate: Optional[bool] = field( - metadata={'dbus_name': 'runner-fast-rate', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'runner-fast-rate', + 'dbus_type': 'b', + }, + default=None, ) + """Corresponds to the teamd runner.fast_rate.""" runner_hwaddr_policy: Optional[str] = field( - metadata={'dbus_name': 'runner-hwaddr-policy', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'runner-hwaddr-policy', + 'dbus_type': 's', + }, default=None, ) + """Corresponds to the teamd runner.hwaddr_policy.""" runner_min_ports: Optional[int] = field( - metadata={'dbus_name': 'runner-min-ports', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'runner-min-ports', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd runner.min_ports.""" runner_sys_prio: Optional[int] = field( - metadata={'dbus_name': 'runner-sys-prio', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'runner-sys-prio', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd runner.sys_prio.""" runner_tx_balancer: Optional[str] = field( - metadata={'dbus_name': 'runner-tx-balancer', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'runner-tx-balancer', + 'dbus_type': 's', + }, default=None, ) + """Corresponds to the teamd runner.tx_balancer.name.""" runner_tx_balancer_interval: Optional[int] = field( - metadata={'dbus_name': 'runner-tx-balancer-interval', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'runner-tx-balancer-interval', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd runner.tx_balancer.interval.""" runner_tx_hash: Optional[List[str]] = field( - metadata={'dbus_name': 'runner-tx-hash', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'runner-tx-hash', + 'dbus_type': 'as', + }, default=None, ) + """Corresponds to the teamd runner.tx_hash.""" diff --git a/sdbus_async/networkmanager/settings/team_port.py b/sdbus_async/networkmanager/settings/team_port.py index 453c42f..65fc1b4 100644 --- a/sdbus_async/networkmanager/settings/team_port.py +++ b/sdbus_async/networkmanager/settings/team_port.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin from .datatypes import LinkWatchers @@ -13,32 +13,71 @@ class TeamPortSettings(NetworkManagerSettingsMixin): """Team Port Settings""" config: Optional[str] = field( - metadata={'dbus_name': 'config', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'config', + 'dbus_type': 's', + }, default=None, ) + """The JSON configuration for the team port. The property should contain + raw JSON configuration data suitable for teamd, because the value is + passed directly to teamd. If not specified, the default + configuration is used. See man teamd.conf for the format details.""" lacp_key: Optional[int] = field( - metadata={'dbus_name': 'lacp-key', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'lacp-key', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd ports.PORTIFNAME.lacp_key.""" lacp_prio: Optional[int] = field( - metadata={'dbus_name': 'lacp-prio', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'lacp-prio', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd ports.PORTIFNAME.lacp_prio.""" link_watchers: Optional[List[LinkWatchers]] = field( - metadata={'dbus_name': 'link-watchers', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': LinkWatchers}, + metadata={ + 'dbus_name': 'link-watchers', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': LinkWatchers, + }, default=None, ) + """Link watchers configuration for the connection: each link watcher is + defined by a dictionary, whose keys depend upon the selected link + watcher. Available link watchers are 'ethtool', 'nsna_ping' and + 'arp_ping' and it is specified in the dictionary with the key + 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', + 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', + 'target-host'; arp_ping: all the ones in nsna_ping and 'source- + host', 'validate-active', 'validate-inactive', 'send-always'. See + teamd.conf man for more details.""" prio: Optional[int] = field( - metadata={'dbus_name': 'prio', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'prio', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd ports.PORTIFNAME.prio.""" queue_id: Optional[int] = field( - metadata={'dbus_name': 'queue-id', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'queue-id', + 'dbus_type': 'i', + }, default=None, ) + """Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means + the parameter is skipped from the json config.""" sticky: Optional[bool] = field( - metadata={'dbus_name': 'sticky', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'sticky', + 'dbus_type': 'b', + }, + default=None, ) + """Corresponds to the teamd ports.PORTIFNAME.sticky.""" diff --git a/sdbus_async/networkmanager/settings/tun.py b/sdbus_async/networkmanager/settings/tun.py index ea834e7..03dee1f 100644 --- a/sdbus_async/networkmanager/settings/tun.py +++ b/sdbus_async/networkmanager/settings/tun.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,26 +12,58 @@ class TunSettings(NetworkManagerSettingsMixin): """Tunnel Settings""" group: Optional[str] = field( - metadata={'dbus_name': 'group', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'group', + 'dbus_type': 's', + }, default=None, ) + """The group ID which will own the device. If set to NULL everyone will be + able to use the device.""" mode: Optional[int] = field( - metadata={'dbus_name': 'mode', 'dbus_type': 'u'}, - default=1, + metadata={ + 'dbus_name': 'mode', + 'dbus_type': 'u', + }, + default=None, ) + """The operating mode of the virtual device. Allowed values are + NM_SETTING_TUN_MODE_TUN (1) to create a layer 3 device and + NM_SETTING_TUN_MODE_TAP (2) to create an Ethernet-like layer 2 one.""" multi_queue: Optional[bool] = field( - metadata={'dbus_name': 'multi-queue', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'multi-queue', + 'dbus_type': 'b', + }, + default=None, ) + """If the property is set to TRUE, the interface will support multiple file + descriptors (queues) to parallelize packet sending or receiving. + Otherwise, the interface will only support a single queue.""" owner: Optional[str] = field( - metadata={'dbus_name': 'owner', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'owner', + 'dbus_type': 's', + }, default=None, ) + """The user ID which will own the device. If set to NULL everyone will be + able to use the device.""" pi: Optional[bool] = field( - metadata={'dbus_name': 'pi', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'pi', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE the interface will prepend a 4 byte header describing the + physical interface to the packets.""" vnet_hdr: Optional[bool] = field( - metadata={'dbus_name': 'vnet-hdr', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'vnet-hdr', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio + network header.""" diff --git a/sdbus_async/networkmanager/settings/user.py b/sdbus_async/networkmanager/settings/user.py index dc223a5..2c3835a 100644 --- a/sdbus_async/networkmanager/settings/user.py +++ b/sdbus_async/networkmanager/settings/user.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import Dict, List, Optional +from typing import Dict, Optional from .base import NetworkManagerSettingsMixin @@ -12,6 +12,13 @@ class UserSettings(NetworkManagerSettingsMixin): """General User Profile Settings""" data: Optional[Dict[str, str]] = field( - metadata={'dbus_name': 'data', 'dbus_type': 'a{ss}'}, + metadata={ + 'dbus_name': 'data', + 'dbus_type': 'a{ss}', + }, default=None, ) + """A dictionary of key/value pairs with user data. This data is ignored by + NetworkManager and can be used at the users discretion. The keys + only support a strict ascii format, but the values can be arbitrary + UTF8 strings up to a certain length.""" diff --git a/sdbus_async/networkmanager/settings/veth.py b/sdbus_async/networkmanager/settings/veth.py index b263845..166393e 100644 --- a/sdbus_async/networkmanager/settings/veth.py +++ b/sdbus_async/networkmanager/settings/veth.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,6 +12,11 @@ class VethSettings(NetworkManagerSettingsMixin): """Veth Settings""" peer: Optional[str] = field( - metadata={'dbus_name': 'peer', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'peer', + 'dbus_type': 's', + }, default=None, ) + """This property specifies the peer interface name of the veth. This + property is mandatory.""" diff --git a/sdbus_async/networkmanager/settings/vlan.py b/sdbus_async/networkmanager/settings/vlan.py index 7e49da0..9fe54e2 100644 --- a/sdbus_async/networkmanager/settings/vlan.py +++ b/sdbus_async/networkmanager/settings/vlan.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,26 +12,71 @@ class VlanSettings(NetworkManagerSettingsMixin): """VLAN Settings""" egress_priority_map: Optional[List[str]] = field( - metadata={'dbus_name': 'egress-priority-map', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'egress-priority-map', + 'dbus_type': 'as', + }, default=None, ) + """For outgoing packets, a list of mappings from Linux SKB priorities to + 802.1p priorities. The mapping is given in the format "from:to" + where both "from" and "to" are unsigned integers, ie "7:3".""" flags: Optional[int] = field( - metadata={'dbus_name': 'flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'flags', + 'dbus_type': 'u', + }, default=None, ) + """One or more flags which control the behavior and features of the VLAN + interface. Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) + (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use + of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose + binding of the interface to its master device's operating state). + NM_VLAN_FLAG_MVRP (0x8) (use of the MVRP protocol). + + The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, + but it used to be 0. To preserve backward compatibility, the + default-value in the D-Bus API continues to be 0 and a missing + property on D-Bus is still considered as 0.""" vlan_id: Optional[int] = field( - metadata={'dbus_name': 'id', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'id', + 'dbus_type': 'u', + }, default=None, ) + """The VLAN identifier that the interface created by this connection should + be assigned. The valid range is from 0 to 4094, without the reserved + id 4095.""" ingress_priority_map: Optional[List[str]] = field( - metadata={'dbus_name': 'ingress-priority-map', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'ingress-priority-map', + 'dbus_type': 'as', + }, default=None, ) + """For incoming packets, a list of mappings from 802.1p priorities to Linux + SKB priorities. The mapping is given in the format "from:to" where + both "from" and "to" are unsigned integers, ie "7:3".""" interface_name: Optional[str] = field( - metadata={'dbus_name': 'interface-name', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'interface-name', + 'dbus_type': 's', + }, default=None, ) + """Deprecated in favor of connection.interface-name, but can be used for + backward-compatibility with older daemons, to set the vlan's + interface name.""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name or parent connection UUID + from which this VLAN interface should be created. If this property + is not specified, the connection must contain an "802-3-ethernet" + setting with a "mac-address" property.""" diff --git a/sdbus_async/networkmanager/settings/vpn.py b/sdbus_async/networkmanager/settings/vpn.py index 19016ea..5089090 100644 --- a/sdbus_async/networkmanager/settings/vpn.py +++ b/sdbus_async/networkmanager/settings/vpn.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import Dict, List, Optional +from typing import Dict, Optional from .base import NetworkManagerSettingsMixin @@ -12,26 +12,65 @@ class VpnSettings(NetworkManagerSettingsMixin): """VPN Settings""" data: Optional[Dict[str, str]] = field( - metadata={'dbus_name': 'data', 'dbus_type': 'a{ss}'}, + metadata={ + 'dbus_name': 'data', + 'dbus_type': 'a{ss}', + }, default=None, ) + """Dictionary of key/value pairs of VPN plugin specific data. Both keys + and values must be strings.""" persistent: Optional[bool] = field( - metadata={'dbus_name': 'persistent', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'persistent', + 'dbus_type': 'b', + }, + default=None, ) + """If the VPN service supports persistence, and this property is TRUE, the + VPN will attempt to stay connected across link changes and outages, + until explicitly disconnected.""" secrets: Optional[Dict[str, str]] = field( - metadata={'dbus_name': 'secrets', 'dbus_type': 'a{ss}'}, + metadata={ + 'dbus_name': 'secrets', + 'dbus_type': 'a{ss}', + }, default=None, ) + """Dictionary of key/value pairs of VPN plugin specific secrets like + passwords or private keys. Both keys and values must be strings.""" service_type: Optional[str] = field( - metadata={'dbus_name': 'service-type', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'service-type', + 'dbus_type': 's', + }, default=None, ) + """D-Bus service name of the VPN plugin that this setting uses to connect + to its network. i.e. org.freedesktop.NetworkManager.vpnc for the + vpnc plugin.""" timeout: Optional[int] = field( - metadata={'dbus_name': 'timeout', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'timeout', + 'dbus_type': 'u', + }, default=None, ) + """Timeout for the VPN service to establish the connection. Some services + may take quite a long time to connect. Value of 0 means a default + timeout, which is 60 seconds (unless overridden by vpn.timeout in + configuration file). Values greater than zero mean timeout in + seconds.""" user_name: Optional[str] = field( - metadata={'dbus_name': 'user-name', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'user-name', + 'dbus_type': 's', + }, default=None, ) + """If the VPN connection requires a user name for authentication, that name + should be provided here. If the connection is available to more + than one user, and the VPN requires each user to supply a different + name, then leave this property empty. If this property is empty, + NetworkManager will automatically supply the username of the user + which requested the VPN connection.""" diff --git a/sdbus_async/networkmanager/settings/vrf.py b/sdbus_async/networkmanager/settings/vrf.py index f312943..1085bb7 100644 --- a/sdbus_async/networkmanager/settings/vrf.py +++ b/sdbus_async/networkmanager/settings/vrf.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,6 +12,10 @@ class VrfSettings(NetworkManagerSettingsMixin): """VRF settings""" table: Optional[int] = field( - metadata={'dbus_name': 'table', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'table', + 'dbus_type': 'u', + }, default=None, ) + """The routing table for this VRF.""" diff --git a/sdbus_async/networkmanager/settings/vxlan.py b/sdbus_async/networkmanager/settings/vxlan.py index e1c2552..3cb3314 100644 --- a/sdbus_async/networkmanager/settings/vxlan.py +++ b/sdbus_async/networkmanager/settings/vxlan.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,66 +12,138 @@ class VxlanSettings(NetworkManagerSettingsMixin): """VXLAN Settings""" ageing: Optional[int] = field( - metadata={'dbus_name': 'ageing', 'dbus_type': 'u'}, - default=300, + metadata={ + 'dbus_name': 'ageing', + 'dbus_type': 'u', + }, + default=None, ) + """Specifies the lifetime in seconds of FDB entries learnt by the kernel.""" destination_port: Optional[int] = field( - metadata={'dbus_name': 'destination-port', 'dbus_type': 'u'}, - default=8472, + metadata={ + 'dbus_name': 'destination-port', + 'dbus_type': 'u', + }, + default=None, ) + """Specifies the UDP destination port to communicate to the remote VXLAN + tunnel endpoint.""" vxlan_id: Optional[int] = field( - metadata={'dbus_name': 'id', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'id', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to + use.""" l2_miss: Optional[bool] = field( - metadata={'dbus_name': 'l2-miss', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'l2-miss', + 'dbus_type': 'b', + }, + default=None, ) + """Specifies whether netlink LL ADDR miss notifications are generated.""" l3_miss: Optional[bool] = field( - metadata={'dbus_name': 'l3-miss', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'l3-miss', + 'dbus_type': 'b', + }, + default=None, ) + """Specifies whether netlink IP ADDR miss notifications are generated.""" learning: Optional[bool] = field( - metadata={'dbus_name': 'learning', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'learning', + 'dbus_type': 'b', + }, + default=None, ) + """Specifies whether unknown source link layer addresses and IP addresses + are entered into the VXLAN device forwarding database.""" limit: Optional[int] = field( - metadata={'dbus_name': 'limit', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'limit', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the maximum number of FDB entries. A value of zero means that + the kernel will store unlimited entries.""" local: Optional[str] = field( - metadata={'dbus_name': 'local', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'local', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the source IP address to use in outgoing packets.""" parent: Optional[str] = field( - metadata={'dbus_name': 'parent', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'parent', + 'dbus_type': 's', + }, default=None, ) + """If given, specifies the parent interface name or parent connection UUID.""" proxy: Optional[bool] = field( - metadata={'dbus_name': 'proxy', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'proxy', + 'dbus_type': 'b', + }, + default=None, ) + """Specifies whether ARP proxy is turned on.""" remote: Optional[str] = field( - metadata={'dbus_name': 'remote', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'remote', + 'dbus_type': 's', + }, default=None, ) + """Specifies the unicast destination IP address to use in outgoing packets + when the destination link layer address is not known in the VXLAN + device forwarding database, or the multicast IP address to join.""" rsc: Optional[bool] = field( - metadata={'dbus_name': 'rsc', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'rsc', + 'dbus_type': 'b', + }, + default=None, ) + """Specifies whether route short circuit is turned on.""" source_port_max: Optional[int] = field( - metadata={'dbus_name': 'source-port-max', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'source-port-max', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the maximum UDP source port to communicate to the remote VXLAN + tunnel endpoint.""" source_port_min: Optional[int] = field( - metadata={'dbus_name': 'source-port-min', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'source-port-min', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the minimum UDP source port to communicate to the remote VXLAN + tunnel endpoint.""" tos: Optional[int] = field( - metadata={'dbus_name': 'tos', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'tos', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the TOS value to use in outgoing packets.""" ttl: Optional[int] = field( - metadata={'dbus_name': 'ttl', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'ttl', + 'dbus_type': 'u', + }, default=None, ) + """Specifies the time-to-live value to use in outgoing packets.""" diff --git a/sdbus_async/networkmanager/settings/wifi_p2p.py b/sdbus_async/networkmanager/settings/wifi_p2p.py index 5c4a644..5d18bd1 100644 --- a/sdbus_async/networkmanager/settings/wifi_p2p.py +++ b/sdbus_async/networkmanager/settings/wifi_p2p.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,14 +12,35 @@ class WifiP2PSettings(NetworkManagerSettingsMixin): """Wi-Fi P2P Settings""" peer: Optional[str] = field( - metadata={'dbus_name': 'peer', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'peer', + 'dbus_type': 's', + }, default=None, ) + """The P2P device that should be connected to. Currently, this is the only + way to create or join a group.""" wfd_ies: Optional[bytes] = field( - metadata={'dbus_name': 'wfd-ies', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'wfd-ies', + 'dbus_type': 'ay', + }, default=None, ) + """The Wi-Fi Display (WFD) Information Elements (IEs) to set. + + Wi-Fi Display requires a protocol specific information element to be + set in certain Wi-Fi frames. These can be specified here for the + purpose of establishing a connection. This setting is only useful + when implementing a Wi-Fi Display client.""" wps_method: Optional[int] = field( - metadata={'dbus_name': 'wps-method', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'wps-method', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating which mode of WPS is to be used. + + There's little point in changing the default setting as + NetworkManager will automatically determine the best method to use.""" diff --git a/sdbus_async/networkmanager/settings/wimax.py b/sdbus_async/networkmanager/settings/wimax.py index 0e67c8e..d498f75 100644 --- a/sdbus_async/networkmanager/settings/wimax.py +++ b/sdbus_async/networkmanager/settings/wimax.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,10 +12,21 @@ class WimaxSettings(NetworkManagerSettingsMixin): """WiMax Settings""" mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, this connection will only apply to the WiMAX device whose + MAC address matches. This property does not change the MAC address + of the device (known as MAC spoofing).""" network_name: Optional[str] = field( - metadata={'dbus_name': 'network-name', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'network-name', + 'dbus_type': 's', + }, default=None, ) + """Network Service Provider (NSP) name of the WiMAX network this connection + should use.""" diff --git a/sdbus_async/networkmanager/settings/wireguard.py b/sdbus_async/networkmanager/settings/wireguard.py index 65e845b..e1f4c32 100644 --- a/sdbus_async/networkmanager/settings/wireguard.py +++ b/sdbus_async/networkmanager/settings/wireguard.py @@ -1,52 +1,126 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field -from typing import List, Optional +from typing import Any, List, Optional, Tuple from .base import NetworkManagerSettingsMixin -from .datatypes import WireguardPeers as Peers +from .datatypes import WireguardPeers @dataclass class WireguardSettings(NetworkManagerSettingsMixin): """WireGuard Settings""" + secret_fields_names = ['private_key'] + secret_name = 'wireguard' fwmark: Optional[int] = field( - metadata={'dbus_name': 'fwmark', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'fwmark', + 'dbus_type': 'u', + }, default=None, ) + """The use of fwmark is optional and is by default off. Setting it to 0 + disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. + + Note that "ip4-auto-default-route" or "ip6-auto-default-route" + enabled, implies to automatically choose a fwmark.""" ip4_auto_default_route: Optional[int] = field( - metadata={'dbus_name': 'ip4-auto-default-route', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'ip4-auto-default-route', + 'dbus_type': 'i', + }, default=None, ) + """Whether to enable special handling of the IPv4 default route. If + enabled, the IPv4 default route from wireguard.peer-routes will be + placed to a dedicated routing-table and two policy routing rules + will be added. The fwmark number is also used as routing-table for + the default-route, and if fwmark is zero, an unused fwmark/table is + chosen automatically. This corresponds to what wg-quick does with + Table=auto and what WireGuard calls "Improved Rule-based Routing". + + Note that for this automatism to work, you usually don't want to set + ipv4.gateway, because that will result in a conflicting default + route. + + Leaving this at the default will enable this option automatically if + ipv4.never-default is not set and there are any peers that use a + default-route as allowed-ips. Since this automatism only makes sense + if you also have a peer with an /0 allowed-ips, it is usually not + necessary to enable this explicitly. However, you can disable it if + you want to configure your own routing and rules.""" ip6_auto_default_route: Optional[int] = field( - metadata={'dbus_name': 'ip6-auto-default-route', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'ip6-auto-default-route', + 'dbus_type': 'i', + }, default=None, ) + """Like ip4-auto-default-route, but for the IPv6 default route.""" listen_port: Optional[int] = field( - metadata={'dbus_name': 'listen-port', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'listen-port', + 'dbus_type': 'u', + }, default=None, ) + """The listen-port. If listen-port is not specified, the port will be + chosen randomly when the interface comes up.""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple fragments. + + If zero a default MTU is used. Note that contrary to wg-quick's MTU + setting, this does not take into account the current routes at the + time of activation.""" peer_routes: Optional[bool] = field( - metadata={'dbus_name': 'peer-routes', 'dbus_type': 'b'}, - default=True, + metadata={ + 'dbus_name': 'peer-routes', + 'dbus_type': 'b', + }, + default=None, ) - peers: Optional[List[Peers]] = field( - metadata={'dbus_name': 'peers', - 'dbus_type': 'aa{sv}', - 'dbus_inner_class': Peers}, + """Whether to automatically add routes for the AllowedIPs ranges of the + peers. If TRUE (the default), NetworkManager will automatically add + routes in the routing tables according to ipv4.route-table and + ipv6.route-table. Usually you want this automatism enabled. If + FALSE, no such routes are added automatically. In this case, the + user may want to configure static routes in ipv4.routes and + ipv6.routes, respectively. + + Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the + profile's ipv4.never-default or ipv6.never-default setting is + enabled, the peer route for this peer won't be added automatically.""" + peers: Optional[List[WireguardPeers]] = field( + metadata={ + 'dbus_name': 'peers', + 'dbus_type': 'aa{sv}', + 'dbus_inner_class': WireguardPeers, + }, default=None, ) + """Array of dictionaries for the WireGuard peers.""" private_key: Optional[str] = field( - metadata={'dbus_name': 'private-key', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'private-key', + 'dbus_type': 's', + }, default=None, ) + """The 256 bit private-key in base64 encoding.""" private_key_flags: Optional[int] = field( - metadata={'dbus_name': 'private-key-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'private-key-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "private-key" property.""" diff --git a/sdbus_async/networkmanager/settings/wireless.py b/sdbus_async/networkmanager/settings/wireless.py index 4d5d165..f2cc165 100644 --- a/sdbus_async/networkmanager/settings/wireless.py +++ b/sdbus_async/networkmanager/settings/wireless.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,82 +12,289 @@ class WirelessSettings(NetworkManagerSettingsMixin): """Wi-Fi Settings""" ap_isolation: Optional[int] = field( - metadata={'dbus_name': 'ap-isolation', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'ap-isolation', + 'dbus_type': 'i', + }, default=None, ) + """Configures AP isolation, which prevents communication between wireless + devices connected to this AP. This property can be set to a value + different from NM_TERNARY_DEFAULT (-1) only when the interface is + configured in AP mode. + + If set to NM_TERNARY_TRUE (1), devices are not able to communicate + with each other. This increases security because it protects devices + against attacks from other clients in the network. At the same time, + it prevents devices to access resources on the same wireless + networks as file shares, printers, etc. + + If set to NM_TERNARY_FALSE (0), devices can talk to each other. + + When set to NM_TERNARY_DEFAULT (-1), the global default is used; in + case the global default is unspecified it is assumed to be + NM_TERNARY_FALSE (0).""" assigned_mac_address: Optional[str] = field( - metadata={'dbus_name': 'assigned-mac-address', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'assigned-mac-address', + 'dbus_type': 's', + }, default=None, ) + """The new field for the cloned MAC address. It can be either a hardware + address in ASCII representation, or one of the special values + "preserve", "permanent", "random" or "stable". This field replaces + the deprecated "cloned-mac-address" on D-Bus, which can only contain + explicit hardware addresses. Note that this property only exists in + D-Bus API. libnm and nmcli continue to call this property "cloned- + mac-address".""" band: Optional[str] = field( - metadata={'dbus_name': 'band', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'band', + 'dbus_type': 's', + }, default=None, ) + """802.11 frequency band of the network. One of "a" for 5GHz 802.11a or + "bg" for 2.4GHz 802.11. This will lock associations to the Wi-Fi + network to the specific band, i.e. if "a" is specified, the device + will not associate with the same network in the 2.4GHz band even if + the network's settings are compatible. This setting depends on + specific driver capability and may not work with all drivers.""" bssid: Optional[bytes] = field( - metadata={'dbus_name': 'bssid', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'bssid', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, directs the device to only associate with the given access + point. This capability is highly driver dependent and not supported + by all devices. Note: this property does not control the BSSID used + when creating an Ad-Hoc network and is unlikely to in the future. + + Locking a client profile to a certain BSSID will prevent roaming and + also disable background scanning. That can be useful, if there is + only one access point for the SSID.""" channel: Optional[int] = field( - metadata={'dbus_name': 'channel', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'channel', + 'dbus_type': 'u', + }, default=None, ) + """Wireless channel to use for the Wi-Fi connection. The device will only + join (or create for Ad-Hoc networks) a Wi-Fi network on the + specified channel. Because channel numbers overlap between bands, + this property also requires the "band" property to be set.""" cloned_mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'cloned-mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'cloned-mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, request that the device use this MAC address instead. This + is known as MAC cloning or spoofing. + + Beside explicitly specifying a MAC address, the special values + "preserve", "permanent", "random" and "stable" are supported. + "preserve" means not to touch the MAC address on activation. + "permanent" means to use the permanent hardware address of the + device. "random" creates a random MAC address on each connect. + "stable" creates a hashed MAC address based on connection.stable-id + and a machine dependent key. + + If unspecified, the value can be overwritten via global defaults, + see manual of NetworkManager.conf. If still unspecified, it defaults + to "preserve" (older versions of NetworkManager may use a different + default value). + + On D-Bus, this field is expressed as "assigned-mac-address" or the + deprecated "cloned-mac-address".""" generate_mac_address_mask: Optional[str] = field( - metadata={'dbus_name': 'generate-mac-address-mask', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'generate-mac-address-mask', + 'dbus_type': 's', + }, default=None, ) + """With "cloned-mac-address" setting "random" or "stable", by default all + bits of the MAC address are scrambled and a locally-administered, + unicast MAC address is created. This property allows to specify that + certain bits are fixed. Note that the least significant bit of the + first MAC address will always be unset to create a unicast MAC + address. + + If the property is NULL, it is eligible to be overwritten by a + default connection setting. If the value is still NULL or an empty + string, the default is to create a locally-administered, unicast MAC + address. + + If the value contains one MAC address, this address is used as mask. + The set bits of the mask are to be filled with the current MAC + address of the device, while the unset bits are subject to + randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI + of the current MAC address and only randomize the lower 3 bytes + using the "random" or "stable" algorithm. + + If the value contains one additional MAC address after the mask, + this address is used instead of the current MAC address to fill the + bits that shall not be randomized. For example, a value of + "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC + address to 68:F7:28, while the lower bits are randomized. A value of + "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + globally-administered, burned-in MAC address. + + If the value contains more than one additional MAC addresses, one of + them is chosen randomly. For example, "02:00:00:00:00:00 + 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled + MAC address, randomly locally or globally administered.""" hidden: Optional[bool] = field( - metadata={'dbus_name': 'hidden', 'dbus_type': 'b'}, - default=False, + metadata={ + 'dbus_name': 'hidden', + 'dbus_type': 'b', + }, + default=None, ) + """If TRUE, indicates that the network is a non-broadcasting network that + hides its SSID. This works both in infrastructure and AP mode. + + In infrastructure mode, various workarounds are used for a more + reliable discovery of hidden networks, such as probe-scanning the + SSID. However, these workarounds expose inherent insecurities with + hidden SSID networks, and thus hidden SSID networks should be used + with caution. + + In AP mode, the created network does not broadcast its SSID. + + Note that marking the network as hidden may be a privacy issue for + you (in infrastructure mode) or client stations (in AP mode), as the + explicit probe-scans are distinctly recognizable on the air.""" mac_address: Optional[bytes] = field( - metadata={'dbus_name': 'mac-address', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'mac-address', + 'dbus_type': 'ay', + }, default=None, ) + """If specified, this connection will only apply to the Wi-Fi device whose + permanent MAC address matches. This property does not change the MAC + address of the device (i.e. MAC spoofing).""" mac_address_blacklist: Optional[List[str]] = field( - metadata={'dbus_name': 'mac-address-blacklist', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'mac-address-blacklist', + 'dbus_type': 'as', + }, default=None, ) + """A list of permanent MAC addresses of Wi-Fi devices to which this + connection should never apply. Each MAC address should be given in + the standard hex-digits-and-colons notation (eg + "00:11:22:33:44:55").""" mac_address_randomization: Optional[int] = field( - metadata={'dbus_name': 'mac-address-randomization', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mac-address-randomization', + 'dbus_type': 'u', + }, default=None, ) + """One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless + the user has set a global default to randomize and the supplicant + supports randomization), NM_SETTING_MAC_RANDOMIZATION_NEVER (1) + (never randomize the MAC address), or + NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC + address).""" mode: Optional[str] = field( - metadata={'dbus_name': 'mode', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'mode', + 'dbus_type': 's', + }, default=None, ) + """Wi-Fi network mode; one of "infrastructure", "mesh", "adhoc" or "ap". + If blank, infrastructure is assumed.""" mtu: Optional[int] = field( - metadata={'dbus_name': 'mtu', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'mtu', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, only transmit packets of the specified size or smaller, + breaking larger packets up into multiple Ethernet frames.""" powersave: Optional[int] = field( - metadata={'dbus_name': 'powersave', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'powersave', + 'dbus_type': 'u', + }, default=None, ) + """One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power + saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi + power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch + currently configure setting) or + NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally + configured value). All other values are reserved.""" rate: Optional[int] = field( - metadata={'dbus_name': 'rate', 'dbus_type': 'u'}, - default=None, - ) - security: Optional[str] = field( - metadata={'dbus_name': 'security', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'rate', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, directs the device to only use the specified bitrate for + communication with the access point. Units are in Kb/s, ie 5500 = + 5.5 Mbit/s. This property is highly driver dependent and not all + devices support setting a static bitrate.""" seen_bssids: Optional[List[str]] = field( - metadata={'dbus_name': 'seen-bssids', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'seen-bssids', + 'dbus_type': 'as', + }, default=None, ) + """A list of BSSIDs (each BSSID formatted as a MAC address like + "00:11:22:33:44:55") that have been detected as part of the Wi-Fi + network. NetworkManager internally tracks previously seen BSSIDs. + The property is only meant for reading and reflects the BSSID list + of NetworkManager. The changes you make to this property will not be + preserved.""" ssid: Optional[bytes] = field( - metadata={'dbus_name': 'ssid', 'dbus_type': 'ay'}, + metadata={ + 'dbus_name': 'ssid', + 'dbus_type': 'ay', + }, default=None, ) + """SSID of the Wi-Fi network. Must be specified.""" tx_power: Optional[int] = field( - metadata={'dbus_name': 'tx-power', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'tx-power', + 'dbus_type': 'u', + }, default=None, ) + """If non-zero, directs the device to use the specified transmit power. + Units are dBm. This property is highly driver dependent and not all + devices support setting a static transmit power.""" wake_on_wlan: Optional[int] = field( - metadata={'dbus_name': 'wake-on-wlan', 'dbus_type': 'u'}, - default=1, + metadata={ + 'dbus_name': 'wake-on-wlan', + 'dbus_type': 'u', + }, + default=None, ) + """The NMSettingWirelessWakeOnWLan options to enable. Not all devices + support all options. May be any combination of + NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY (0x2), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT (0x4), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC (0x8), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE (0x10), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST (0x20), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE (0x40), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE (0x80), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP (0x100) or the special values + NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT (0x1) (to use global + settings) and NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE (0x8000) (to + disable management of Wake-on-LAN in NetworkManager).""" diff --git a/sdbus_async/networkmanager/settings/wireless_security.py b/sdbus_async/networkmanager/settings/wireless_security.py index 926298b..2c1dda1 100644 --- a/sdbus_async/networkmanager/settings/wireless_security.py +++ b/sdbus_async/networkmanager/settings/wireless_security.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -10,84 +10,234 @@ @dataclass class WirelessSecuritySettings(NetworkManagerSettingsMixin): """Wi-Fi Security Settings""" + secret_fields_names = ['leap_password', 'psk'] + secret_name = '802-11-wireless-security' auth_alg: Optional[str] = field( - metadata={'dbus_name': 'auth-alg', 'dbus_type': 's'}, - default=None, - ) + metadata={ + 'dbus_name': 'auth-alg', + 'dbus_type': 's', + }, + default=None, + ) + """When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the + 802.11 authentication algorithm required by the AP here. One of + "open" for Open System, "shared" for Shared Key, or "leap" for Cisco + LEAP. When using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth- + alg = "leap") the "leap-username" and "leap-password" properties + must be specified.""" fils: Optional[int] = field( - metadata={'dbus_name': 'fils', 'dbus_type': 'i'}, - default=None, - ) + metadata={ + 'dbus_name': 'fils', + 'dbus_type': 'i', + }, + default=None, + ) + """Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for + the connection. One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT + (0) (use global default value), + NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), + NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the + supplicant and the access point support it) or + NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail + if not supported). When set to + NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default + is set, FILS will be optionally enabled.""" group: Optional[List[str]] = field( - metadata={'dbus_name': 'group', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'group', + 'dbus_type': 'as', + }, default=None, ) + """A list of group/broadcast encryption algorithms which prevents + connections to Wi-Fi networks that do not utilize one of the + algorithms in the list. For maximum compatibility leave this + property empty. Each list element may be one of "wep40", "wep104", + "tkip", or "ccmp".""" key_mgmt: Optional[str] = field( - metadata={'dbus_name': 'key-mgmt', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'key-mgmt', + 'dbus_type': 's', + }, default=None, ) + """Key management used for the connection. One of "none" (WEP or no + password protection), "ieee8021x" (Dynamic WEP), "owe" + (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 + personal), "sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 + enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only). + + This property must be set for any Wi-Fi connection that uses + security.""" leap_password: Optional[str] = field( - metadata={'dbus_name': 'leap-password', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'leap-password', + 'dbus_type': 's', + }, default=None, ) + """The login password for legacy LEAP connections (ie, key-mgmt = + "ieee8021x" and auth-alg = "leap").""" leap_password_flags: Optional[int] = field( - metadata={'dbus_name': 'leap-password-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'leap-password-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "leap-password" property.""" leap_username: Optional[str] = field( - metadata={'dbus_name': 'leap-username', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'leap-username', + 'dbus_type': 's', + }, default=None, ) + """The login username for legacy LEAP connections (ie, key-mgmt = + "ieee8021x" and auth-alg = "leap").""" pairwise: Optional[List[str]] = field( - metadata={'dbus_name': 'pairwise', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'pairwise', + 'dbus_type': 'as', + }, default=None, ) + """A list of pairwise encryption algorithms which prevents connections to + Wi-Fi networks that do not utilize one of the algorithms in the + list. For maximum compatibility leave this property empty. Each + list element may be one of "tkip" or "ccmp".""" pmf: Optional[int] = field( - metadata={'dbus_name': 'pmf', 'dbus_type': 'i'}, - default=None, - ) + metadata={ + 'dbus_name': 'pmf', + 'dbus_type': 'i', + }, + default=None, + ) + """Indicates whether Protected Management Frames (802.11w) must be enabled + for the connection. One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT + (0) (use global default value), + NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), + NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the + supplicant and the access point support it) or + NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail + if not supported). When set to + NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default + is set, PMF will be optionally enabled.""" proto: Optional[List[str]] = field( - metadata={'dbus_name': 'proto', 'dbus_type': 'as'}, + metadata={ + 'dbus_name': 'proto', + 'dbus_type': 'as', + }, default=None, ) + """List of strings specifying the allowed WPA protocol versions to use. + Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN). + If not specified, both WPA and RSN connections are allowed.""" psk: Optional[str] = field( - metadata={'dbus_name': 'psk', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'psk', + 'dbus_type': 's', + }, default=None, ) + """Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII + passphrase of 8 to 63 characters that is (as specified in the + 802.11i standard) hashed to derive the actual key, or the key in + form of 64 hexadecimal character. The WPA3-Personal networks use a + passphrase of any length for SAE authentication.""" psk_flags: Optional[int] = field( - metadata={'dbus_name': 'psk-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'psk-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "psk" property.""" wep_key_flags: Optional[int] = field( - metadata={'dbus_name': 'wep-key-flags', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'wep-key-flags', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating how to handle the "wep-key0", "wep-key1", "wep-key2", + and "wep-key3" properties.""" wep_key_type: Optional[int] = field( - metadata={'dbus_name': 'wep-key-type', 'dbus_type': 'i'}, - default=None, - ) + metadata={ + 'dbus_name': 'wep-key-type', + 'dbus_type': 'u', + }, + default=None, + ) + """Controls the interpretation of WEP keys. Allowed values are + NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or + 26-character hexadecimal string, or a 5- or 13-character ASCII + password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the + passphrase is provided as a string and will be hashed using the de- + facto MD5 method to derive the actual WEP key.""" wep_key0: Optional[str] = field( - metadata={'dbus_name': 'wep-key0', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'wep-key0', + 'dbus_type': 's', + }, default=None, ) + """Index 0 WEP key. This is the WEP key used in most networks. See the + "wep-key-type" property for a description of how this key is + interpreted.""" wep_key1: Optional[str] = field( - metadata={'dbus_name': 'wep-key1', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'wep-key1', + 'dbus_type': 's', + }, default=None, ) + """Index 1 WEP key. This WEP index is not used by most networks. See the + "wep-key-type" property for a description of how this key is + interpreted.""" wep_key2: Optional[str] = field( - metadata={'dbus_name': 'wep-key2', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'wep-key2', + 'dbus_type': 's', + }, default=None, ) + """Index 2 WEP key. This WEP index is not used by most networks. See the + "wep-key-type" property for a description of how this key is + interpreted.""" wep_key3: Optional[str] = field( - metadata={'dbus_name': 'wep-key3', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'wep-key3', + 'dbus_type': 's', + }, default=None, ) + """Index 3 WEP key. This WEP index is not used by most networks. See the + "wep-key-type" property for a description of how this key is + interpreted.""" wep_tx_keyidx: Optional[int] = field( - metadata={'dbus_name': 'wep-tx-keyidx', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'wep-tx-keyidx', + 'dbus_type': 'u', + }, default=None, ) + """When static WEP is used (ie, key-mgmt = "none") and a non-default WEP + key index is used by the AP, put that WEP key index here. Valid + values are 0 (default key) through 3. Note that some consumer + access points (like the Linksys WRT54G) number the keys 1 - 4.""" wps_method: Optional[int] = field( - metadata={'dbus_name': 'wps-method', 'dbus_type': 'u'}, + metadata={ + 'dbus_name': 'wps-method', + 'dbus_type': 'u', + }, default=None, ) + """Flags indicating which mode of WPS is to be used if any. + + There's little point in changing the default setting as + NetworkManager will automatically determine whether it's feasible to + start WPS enrollment from the Access Point capabilities. + + WPS can be disabled by setting this property to a value of 1.""" diff --git a/sdbus_async/networkmanager/settings/wpan.py b/sdbus_async/networkmanager/settings/wpan.py index aa79a0a..1c63388 100644 --- a/sdbus_async/networkmanager/settings/wpan.py +++ b/sdbus_async/networkmanager/settings/wpan.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# This file was generated by tools/generate-settings-dataclasses.py, +# This file was generated by tools/generate-settings-dataclasses-jinja.py, # if possible, please make changes by also updating the script. from __future__ import annotations from dataclasses import dataclass, field @@ -12,22 +12,45 @@ class WpanSettings(NetworkManagerSettingsMixin): """IEEE 802.15.4 (WPAN) MAC Settings""" channel: Optional[int] = field( - metadata={'dbus_name': 'channel', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'channel', + 'dbus_type': 'i', + }, default=None, ) + """IEEE 802.15.4 channel. A positive integer or -1, meaning "do not set, + use whatever the device is already set to".""" mac_address: Optional[str] = field( - metadata={'dbus_name': 'mac-address', 'dbus_type': 's'}, + metadata={ + 'dbus_name': 'mac-address', + 'dbus_type': 's', + }, default=None, ) + """If specified, this connection will only apply to the IEEE 802.15.4 + (WPAN) MAC layer device whose permanent MAC address matches.""" page: Optional[int] = field( - metadata={'dbus_name': 'page', 'dbus_type': 'i'}, + metadata={ + 'dbus_name': 'page', + 'dbus_type': 'i', + }, default=None, ) + """IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not + set, use whatever the device is already set to".""" pan_id: Optional[int] = field( - metadata={'dbus_name': 'pan-id', 'dbus_type': 'u'}, - default=65535, + metadata={ + 'dbus_name': 'pan-id', + 'dbus_type': 'u', + }, + default=None, ) + """IEEE 802.15.4 Personal Area Network (PAN) identifier.""" short_address: Optional[int] = field( - metadata={'dbus_name': 'short-address', 'dbus_type': 'u'}, - default=65535, + metadata={ + 'dbus_name': 'short-address', + 'dbus_type': 'u', + }, + default=None, ) + """Short IEEE 802.15.4 address to be used within a restricted environment.""" diff --git a/sdbus_async/networkmanager/types.py b/sdbus_async/networkmanager/types.py index fc9c373..a7bd55e 100644 --- a/sdbus_async/networkmanager/types.py +++ b/sdbus_async/networkmanager/types.py @@ -25,8 +25,8 @@ # A settings domain, e.g. ipv4.*, ipv6.*, 802-11-wireless-security.*, etc: NetworkManagerSettingsDomain = Dict[str, NetworkManagerSetting] -# All settings and properites of a connection, e.g. returned by get_settings() +# All settings and properties of a connection, e.g. returned by get_settings() NetworkManagerConnectionProperties = Dict[str, NetworkManagerSettingsDomain] -# All settings and properites of a connection, but without dbus signatures +# All settings and properties of a connection, but without dbus signatures SettingsDict = Dict[str, Dict[str, Any]] diff --git a/sdbus_block/networkmanager/__init__.py b/sdbus_block/networkmanager/__init__.py index bcdfec0..6b06a65 100644 --- a/sdbus_block/networkmanager/__init__.py +++ b/sdbus_block/networkmanager/__init__.py @@ -20,30 +20,43 @@ from __future__ import annotations from .enums import ( - AccessPointCapabilities, - BluetoothCapabilities, - ConnectionFlags, - ConnectionState, - ConnectionStateFlags, - ConnectionStateReason, + ActivationStateFlags, + ActiveConnectionState, + ActiveConnectionStateReason, + BluetoothCapabilitiesFlags, + CheckpointCreateFlags, + CheckpointRollbackResult, + ConnectionMultiConnect, ConnectionType, ConnectivityState, - DeviceCapabilities, + DeviceCapabilitiesFlags, DeviceInterfaceFlags, DeviceMetered, + DeviceReapplyFlags, DeviceState, DeviceStateReason, DeviceType, IpTunnelMode, - ModemCapabilities, - NetworkManagerConnectivityState, + ModemCapabilitiesFlags, + MptcpFlags, + NetworkManagerCapabilitiesFlags, + NetworkManagerReloadFlags, NetworkManagerState, - SecretAgentCapabilities, + RadioFlags, + SecretAgentCapabilitiesFlags, + SecretAgentGetSecretsFlags, + SettingsAddConnection2Flags, + SettingsConnectionFlags, + SettingsUpdate2Flags, + VpnConnectionState, + VpnConnectionStateReason, VpnFailure, - VpnState, + VpnServiceState, + WifiAccessPointCapabilitiesFlags, + WifiAccessPointSecurityFlags, + WifiCapabilitiesFlags, WiFiOperationMode, - WirelessCapabilities, - WpaSecurityFlags, + WimaxNSPNetworkType, ) from .exceptions import ( NetworkManagerAlreadyAsleepOrAwakeError, @@ -136,6 +149,7 @@ NetworkManagerDeviceWiredInterface, NetworkManagerDeviceWireGuardInterface, NetworkManagerDeviceWirelessInterface, + NetworkManagerLoopbackInterface, NetworkManagerPPPInterface, ) from .interfaces_other import ( @@ -171,6 +185,7 @@ NetworkDeviceBridge, NetworkDeviceGeneric, NetworkDeviceIpTunnel, + NetworkDeviceLoopback, NetworkDeviceMacsec, NetworkDeviceMacvlan, NetworkDeviceModem, @@ -194,55 +209,6 @@ NetworkManagerSettings, WiFiP2PPeer, ) -from .settings.adsl import AdslSettings -from .settings.bluetooth import BluetoothSettings -from .settings.bond import BondSettings -from .settings.bond_port import BondPortSettings -from .settings.bridge import BridgeSettings -from .settings.bridge_port import BridgePortSettings -from .settings.cdma import CdmaSettings -from .settings.connection import ConnectionSettings -from .settings.datatypes import AddressData, RouteData, WireguardPeers -from .settings.dcb import DcbSettings -from .settings.ethernet import EthernetSettings -from .settings.gsm import GsmSettings -from .settings.hostname import HostnameSettings -from .settings.ieee802_1x import Ieee8021XSettings -from .settings.infiniband import InfinibandSettings -from .settings.ip_tunnel import IpTunnelSettings -from .settings.ipv4 import Ipv4Settings -from .settings.ipv6 import Ipv6Settings -from .settings.lowpan import LowpanSettings -from .settings.macsec import MacsecSettings -from .settings.macvlan import MacvlanSettings -from .settings.match import MatchSettings -from .settings.olpc_mesh import OlpcMeshSettings -from .settings.ovs_bridge import OvsBridgeSettings -from .settings.ovs_dpdk import OvsDpdkSettings -from .settings.ovs_external_ids import OvsExternalIdsSettings -from .settings.ovs_interface import OvsInterfaceSettings -from .settings.ovs_patch import OvsPatchSettings -from .settings.ovs_port import OvsPortSettings -from .settings.ppp import PppSettings -from .settings.pppoe import PppoeSettings -from .settings.profile import ConnectionProfile -from .settings.proxy import ProxySettings -from .settings.serial import SerialSettings -from .settings.team import TeamSettings -from .settings.team_port import TeamPortSettings -from .settings.tun import TunSettings -from .settings.user import UserSettings -from .settings.veth import VethSettings -from .settings.vlan import VlanSettings -from .settings.vpn import VpnSettings -from .settings.vrf import VrfSettings -from .settings.vxlan import VxlanSettings -from .settings.wifi_p2p import WifiP2PSettings -from .settings.wimax import WimaxSettings -from .settings.wireguard import WireguardSettings -from .settings.wireless import WirelessSettings -from .settings.wireless_security import WirelessSecuritySettings -from .settings.wpan import WpanSettings from .types import ( NetworkManagerConnectionProperties, NetworkManagerSetting, @@ -265,30 +231,43 @@ __all__ = ( # .enums - 'AccessPointCapabilities', - 'BluetoothCapabilities', - 'ConnectionFlags', - 'ConnectionState', - 'ConnectionStateFlags', - 'ConnectionStateReason', + 'ActivationStateFlags', + 'ActiveConnectionState', + 'ActiveConnectionStateReason', + 'BluetoothCapabilitiesFlags', + 'CheckpointCreateFlags', + 'CheckpointRollbackResult', + 'ConnectionMultiConnect', 'ConnectionType', 'ConnectivityState', - 'DeviceCapabilities', + 'DeviceCapabilitiesFlags', 'DeviceInterfaceFlags', 'DeviceMetered', + 'DeviceReapplyFlags', 'DeviceState', 'DeviceStateReason', 'DeviceType', 'IpTunnelMode', - 'ModemCapabilities', - 'NetworkManagerConnectivityState', + 'ModemCapabilitiesFlags', + 'MptcpFlags', + 'NetworkManagerCapabilitiesFlags', + 'NetworkManagerReloadFlags', 'NetworkManagerState', - 'SecretAgentCapabilities', + 'RadioFlags', + 'SecretAgentCapabilitiesFlags', + 'SecretAgentGetSecretsFlags', + 'SettingsAddConnection2Flags', + 'SettingsConnectionFlags', + 'SettingsUpdate2Flags', + 'VpnConnectionState', + 'VpnConnectionStateReason', 'VpnFailure', - 'VpnState', + 'VpnServiceState', + 'WifiAccessPointCapabilitiesFlags', + 'WifiAccessPointSecurityFlags', + 'WifiCapabilitiesFlags', 'WiFiOperationMode', - 'WirelessCapabilities', - 'WpaSecurityFlags', + 'WimaxNSPNetworkType', # .exceptions 'NetworkManagerAlreadyAsleepOrAwakeError', 'NetworkManagerAlreadyEnabledOrDisabledError', @@ -379,6 +358,7 @@ 'NetworkManagerDeviceWiredInterface', 'NetworkManagerDeviceWireGuardInterface', 'NetworkManagerDeviceWirelessInterface', + 'NetworkManagerLoopbackInterface', 'NetworkManagerPPPInterface', # .interfaces_other 'NetworkManagerAccessPointInterface', @@ -412,6 +392,7 @@ 'NetworkDeviceBridge', 'NetworkDeviceGeneric', 'NetworkDeviceIpTunnel', + 'NetworkDeviceLoopback', 'NetworkDeviceMacsec', 'NetworkDeviceMacvlan', 'NetworkDeviceModem', @@ -434,56 +415,6 @@ 'NetworkManagerDnsManager', 'NetworkManagerSettings', 'WiFiP2PPeer', - # .settings - 'AdslSettings', - 'BluetoothSettings', - 'BondSettings', - 'BondPortSettings', - 'BridgeSettings', - 'BridgePortSettings', - 'CdmaSettings', - 'ConnectionSettings', - 'AddressData', 'RouteData', 'WireguardPeers', - 'DcbSettings', - 'EthernetSettings', - 'GsmSettings', - 'HostnameSettings', - 'Ieee8021XSettings', - 'InfinibandSettings', - 'IpTunnelSettings', - 'Ipv4Settings', - 'Ipv6Settings', - 'LowpanSettings', - 'MacsecSettings', - 'MacvlanSettings', - 'MatchSettings', - 'OlpcMeshSettings', - 'OvsBridgeSettings', - 'OvsDpdkSettings', - 'OvsExternalIdsSettings', - 'OvsInterfaceSettings', - 'OvsPatchSettings', - 'OvsPortSettings', - 'PppSettings', - 'PppoeSettings', - 'ConnectionProfile', - 'ProxySettings', - 'SerialSettings', - 'TeamSettings', - 'TeamPortSettings', - 'TunSettings', - 'UserSettings', - 'VethSettings', - 'VlanSettings', - 'VpnSettings', - 'VrfSettings', - 'VxlanSettings', - 'WifiP2PSettings', - 'WimaxSettings', - 'WireguardSettings', - 'WirelessSettings', - 'WirelessSecuritySettings', - 'WpanSettings', # .types 'NetworkManagerConnectionProperties', 'NetworkManagerSetting', diff --git a/sdbus_block/networkmanager/interfaces_devices.py b/sdbus_block/networkmanager/interfaces_devices.py index 0275bad..ea65082 100644 --- a/sdbus_block/networkmanager/interfaces_devices.py +++ b/sdbus_block/networkmanager/interfaces_devices.py @@ -23,6 +23,8 @@ from sdbus import DbusInterfaceCommon, dbus_method, dbus_property +from .settings import ConnectionProfile + class NetworkManagerDeviceBluetoothInterface( DbusInterfaceCommon, @@ -985,6 +987,41 @@ def hw_address(self) -> str: """Hardware address""" raise NotImplementedError + def get_applied_connection_profile( + self + ) -> Tuple[ConnectionProfile, int]: + """Get the currently applied connection on the device. + + .. note:: + + This method cannot fetch secrets. Use + :py:meth:`NetworkManagerSettingsConnectionInterfaceAsync.get_profile` + to acquire profile with secrets. + + :returns: Tuple of profile and version id. + :rtype: Tuple[ConnectionProfile, int] + """ + + connection_vardict, version_id = self.get_applied_connection(0) + + return ConnectionProfile.from_dbus(connection_vardict), version_id + + def reapply_profile( + self, + profile: ConnectionProfile, + version_id: int = 0, + ) -> None: + """Attempts to update the configuration of a device + without deactivating it. + + :param ConnectionProfile profile: Connection profile to update + with. + :param int version_id: If non-zero, the current version id of + the applied-connection must match. The current version id can be + retrieved via :py:meth:`get_applied_connection_profile`. + """ + self.reapply(profile.to_dbus(), version_id, 0) + class NetworkManagerPPPInterface( DbusInterfaceCommon, @@ -1040,3 +1077,10 @@ def set_ifindex( Not documented upstream. """ raise NotImplementedError + + +class NetworkManagerLoopbackInterface( + DbusInterfaceCommon, + interface_name='org.freedesktop.NetworkManager.Device.Loopback', +): + ... diff --git a/sdbus_block/networkmanager/interfaces_other.py b/sdbus_block/networkmanager/interfaces_other.py index 9ed146a..a65da27 100644 --- a/sdbus_block/networkmanager/interfaces_other.py +++ b/sdbus_block/networkmanager/interfaces_other.py @@ -23,6 +23,7 @@ from sdbus import DbusInterfaceCommon, dbus_method, dbus_property +from .settings import ConnectionProfile from .types import NetworkManagerConnectionProperties @@ -298,7 +299,7 @@ class NetworkManagerDHCP6ConfigInterface( @dbus_property('a{sv}') def options(self) -> Dict[str, Tuple[str, Any]]: - """Options and configurations returned by DHCPv4 server""" + """Options and configurations returned by DHCPv6 server""" raise NotImplementedError @@ -581,6 +582,52 @@ def filename(self) -> str: """File that stores connection settings""" raise NotImplementedError + def update_profile( + self, + profile: ConnectionProfile, + save_to_disk: bool = False, + ) -> None: + """Update connection using the profile dataclass. + + :param ConnectionProfile profile: Connection profile to update + with. + + :param bool save_to_disk: Make changes permanent by saving + updated values to disk. + + By default changes are temporary. (saved only to RAM) + """ + flags = 0 + + if save_to_disk: + flags |= 0x1 + else: + flags |= 0x2 + + self.update2(profile.to_dbus(), flags, {}) + + def get_profile(self, fetch_secrets: bool = True) -> ConnectionProfile: + """Get the connection settings as the profile object. + + :param bool fetch_secrets: Retrieve secret values. (like VPN passwords) + Makes additional calls to NetworkManager. + """ + profile = ConnectionProfile.from_dbus(self.get_settings()) + + if fetch_secrets: + secrets_name_generator = profile.update_secrets_generator() + try: + secrets_name = next(secrets_name_generator) + while True: + secret_profile = ConnectionProfile.from_dbus( + self.get_secrets(secrets_name)) + + secrets_name = secrets_name_generator.send(secret_profile) + except StopIteration: + ... + + return profile + class NetworkManagerSettingsInterface( DbusInterfaceCommon, @@ -691,6 +738,36 @@ def can_modify(self) -> bool: """If true adding and modifying connections is supported""" raise NotImplementedError + def add_connection_profile( + self, + profile: ConnectionProfile, + save_to_disk: bool = False, + ) -> Tuple[str, None]: + """Add new connection using the profile object. + + :param ConnectionProfile profile: Connection profile to update + with. + + :param bool save_to_disk: Make changes permanent by saving + updated values to disk. + + By default changes are temporary. (saved only to RAM) + + :return: Object path of new connection and None + :rtype: Tuple[str, None] + """ + flags = 0 + + if save_to_disk: + flags |= 0x1 + else: + flags |= 0x2 + + return ( + (self.add_connection2(profile.to_dbus(), flags, {}))[0], + None, + ) + class NetworkManagerVPNPluginInterface( DbusInterfaceCommon, @@ -1194,3 +1271,33 @@ def connectivity_check_uri(self) -> str: def global_dns_configuration(self) -> Dict[str, Tuple[str, Any]]: """Global DNS connection settings""" raise NotImplementedError + + def add_and_activate_connection_profile( + self, + profile: ConnectionProfile, + device_path: str = '/', + specific_object: str = '/', + ) -> Tuple[str, str]: + """Adds new connection using the profile object as template. + + :param ConnectionProfile profile: Connection profile to update + with. + + :param str device_path: Object path of device to be activated + using the given connection + + :param str specific_object: The path of a connection-type-specific + object this activation should use. + + This parameter is currently ignored for wired and mobile broadband + connections, and the value of "/" should be used. + + :return: Object path of new connection and path of active connection. + :rtype: Tuple[str, str] + """ + + return self.add_and_activate_connection( + profile.to_dbus(), + device_path, + specific_object, + ) diff --git a/sdbus_block/networkmanager/objects.py b/sdbus_block/networkmanager/objects.py index d7a47bd..3d4c68d 100644 --- a/sdbus_block/networkmanager/objects.py +++ b/sdbus_block/networkmanager/objects.py @@ -49,6 +49,7 @@ NetworkManagerDeviceWiredInterface, NetworkManagerDeviceWireGuardInterface, NetworkManagerDeviceWirelessInterface, + NetworkManagerLoopbackInterface, NetworkManagerPPPInterface, ) from .interfaces_other import ( @@ -67,12 +68,13 @@ NetworkManagerVPNConnectionInterface, NetworkManagerWifiP2PPeerInterface, ) +from .types import NetworkManagerConnectionProperties NETWORK_MANAGER_SERVICE_NAME = 'org.freedesktop.NetworkManager' class NetworkManager(NetworkManagerInterface): - """Network Manger main object + """Network Manager main object Implements :py:class:`NetworkManagerInterface` @@ -158,7 +160,7 @@ def get_connections_by_id(self, connection_id: str) -> List[str]: which use the given connection identifier. :param str connection_id: The connection identifier of the connections, - e.g. "Ethernet connection 1" + e.g. "Wired connection 1" :return: List of connection profile paths using the given identifier. """ connection_paths_with_matching_id = [] @@ -169,6 +171,25 @@ def get_connections_by_id(self, connection_id: str) -> List[str]: connection_paths_with_matching_id.append(connection_path) return connection_paths_with_matching_id + def get_settings_by_uuid( + self, connection_uuid: str + ) -> NetworkManagerConnectionProperties: + """Helper to get a nested settings dict of a connection profile by uuid. + + :param str connection_uuid: The connection uuid of the connection profile + :return: Nested dictionary of all settings of the given connection profile + """ + connection = self.get_connection_by_uuid(connection_uuid) + return NetworkConnectionSettings(connection).get_settings() + + def delete_connection_by_uuid(self, connection_uuid: str) -> None: + """Helper to delete a connection profile identified by the connection uuid. + + :param str connection_uuid: The connection uuid of the connection profile + """ + conn_dbus_path = self.get_connection_by_uuid(connection_uuid) + NetworkConnectionSettings(conn_dbus_path).delete() + class NetworkConnectionSettings( NetworkManagerSettingsConnectionInterface): @@ -809,6 +830,34 @@ def __init__(self, device_path: str, bus: Optional[SdBus] = None) -> None: bus) +class NetworkDeviceLoopback( + NetworkManagerDeviceInterface, + NetworkManagerDeviceStatisticsInterface, + NetworkManagerLoopbackInterface): + """Loopback device + + Implements :py:class:`NetworkManagerDeviceInterface`, \ + :py:class:`NetworkManagerDeviceStatisticsInterface` and \ + :py:class:`NetworkManagerLoopbackInterface` + """ + + def __init__(self, device_path: str, bus: Optional[SdBus] = None) -> None: + """ + + :param device_path: D-Bus path to device object. \ + Obtained from \ + :py:meth:`NetworkManagerInterface.get_devices` or \ + :py:meth:`NetworkManagerInterface.get_device_by_ip_iface`. + + :param bus: You probably want to set default bus to system bus \ + or pass system bus directly. + """ + super().__init__( + NETWORK_MANAGER_SERVICE_NAME, + device_path, + bus) + + class ActiveConnection(NetworkManagerConnectionActiveInterface): """Active connection object diff --git a/setup.py b/setup.py index 5ce16f6..02c1185 100644 --- a/setup.py +++ b/setup.py @@ -29,16 +29,16 @@ description=('NetworkManager binds for sdbus.'), long_description=long_description, long_description_content_type='text/markdown', - version='1.2.0', - url='https://github.com/igo95862/python-sdbus', + version='2.0.0', + 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', @@ -54,6 +54,7 @@ packages=['sdbus_async.networkmanager', 'sdbus_async.networkmanager.settings', 'sdbus_block.networkmanager', + 'sdbus_block.networkmanager.settings', ], package_data={ 'sdbus_async.networkmanager': [ diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..4b6332c --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Copyright (C) 2022 igo95862 + +# This file is part of python-sdbus + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# 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 diff --git a/tests/test_settings_profile.py b/tests/test_settings_profile.py new file mode 100644 index 0000000..8ee6c98 --- /dev/null +++ b/tests/test_settings_profile.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Copyright (C) 2022 igo95862 + +# This file is part of python-sdbus + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# 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 +from __future__ import annotations + +from unittest import TestCase +from sdbus_async.networkmanager.settings import ConnectionProfile + +connection_dict = { + 'connection': {'id': 'mlvd-wg', + 'interface-name': 'mlvd-wg', + 'timestamp': 150000, + 'type': 'wireguard', + 'uuid': 'uuid'}, + 'ipv4': {'address-data': [{'address': '10.0.0.1', 'prefix': 32}], + 'dns': [150000], + 'dns-search': ['~'], + 'method': 'manual'}, + 'ipv6': {'addr-gen-mode': 1, + 'address-data': [{'address': 'fc00:1', + 'prefix': 128}], + 'method': 'manual'}, + 'wireguard': { + 'peers': [ + {'allowed-ips': ['::/0', '0.0.0.0/0'], + 'endpoint': '1.1.1.1:51820', + 'public-key': 'public_key'}]}} + +secret_dict = { + 'connection': {}, + 'ipv4': {}, + 'ipv6': {}, + 'proxy': {}, + 'wireguard': { + 'peers': [ + {'public-key': 'public_key'} + ], + 'private-key': 'secret_key'}} + + +class TestSettingsProfile(TestCase): + def test_update(self) -> None: + connection = ConnectionProfile.from_settings_dict(connection_dict) + secrets = ConnectionProfile.from_settings_dict(secret_dict) + + connection.update(secrets) + + self.assertEqual(connection.wireguard.private_key, 'secret_key') + + def test_update_secrets(self) -> None: + connection = ConnectionProfile.from_settings_dict(connection_dict) + secrets = ConnectionProfile.from_settings_dict(secret_dict) + + connection_secret_update_generator = ( + connection.update_secrets_generator() + ) + + setting_name = next(connection_secret_update_generator) + self.assertEqual(setting_name, 'wireguard') + + with self.assertRaises(StopIteration): + connection_secret_update_generator.send(secrets) + + self.assertEqual(connection.wireguard.private_key, 'secret_key') diff --git a/tools/generate-settings-dataclasses-jinja.py b/tools/generate-settings-dataclasses-jinja.py new file mode 100644 index 0000000..09eca55 --- /dev/null +++ b/tools/generate-settings-dataclasses-jinja.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Gerating nm-settings-docs-dbus.xml from NetworkManager source code +# 1. meson setup \ +# -Dselinux=false \ +# -Dqt=false \ +# -Dintrospection=true \ +# -Ddocs=true \ +# build +# 2. cd build +# 3. ninja man/nm-settings-docs-dbus.xml +from __future__ import annotations + +import builtins +import keyword +from argparse import ArgumentParser +from functools import cached_property +from itertools import dropwhile +from pathlib import Path +from re import Pattern +from re import compile as regex_compile +from textwrap import fill +from typing import List, Optional, Set +from xml.etree.ElementTree import Element, parse + +from jinja2 import Environment, FileSystemLoader, StrictUndefined + +dbus_to_python_extra_typing_imports = { + "as": ("List", ), + "au": ("List", ), + "a{ss}": ("Dict", ), + "aa{sv}": ("List", "Tuple", "Any"), + "aau": ("List", ), + "aay": ("List", ) +} + +dbus_to_python_type_map = { + "b": "bool", + "s": "str", + "i": "int", + "u": "int", + "t": "int", + "x": "int", + "y": "int", + "as": "List[str]", + "au": "List[int]", + "ay": "bytes", + "a{ss}": "Dict[str, str]", + "aa{sv}": "List[Tuple[str, Any]]", + "aau": "List[List[int]]", + "aay": "List[bytes]", +} + +dbus_name_type_map = { + 'array of array of uint32': 'aau', + 'array of byte array': 'aay', + 'array of legacy IPv6 address struct': 'a(ayuay)', + 'array of legacy IPv6 address struct (a(ayuay))': 'a(ayuay)', + 'array of legacy IPv6 route struct': 'a(ayuayu)', + 'array of legacy IPv6 route struct (a(ayuayu))': 'a(ayuayu)', + 'array of string': 'as', + 'array of strings': 'as', + 'array of uint32': 'au', + 'array of vardict': 'aa{sv}', + "array of 'a{sv}'": 'aa{sv}', # wireguard.peers uses this, fix NM upstream + 'boolean': 'b', + 'byte': 'y', + 'byte array': 'ay', + 'dict of string to string': 'a{ss}', + 'int32': 'i', + 'int64': 'x', + 'string': 's', + 'uint32': 'u', + 'uint64': 't', +} + +python_name_replacements = { + 'type': 'connection_type', + 'id': 'pretty_id', +} + + +array_of_vardicts_python_classes: dict[str, str] = { + 'peers': 'WireguardPeers', + 'vlans': 'Vlans', + 'address-data': 'AddressData', + 'route-data': 'RouteData', + 'routing-rules': 'RoutingRules', + 'vfs': 'Vfs', + 'qdiscs': 'Qdiscs', + 'tfilters': 'Tfilters', + 'link-watchers': 'LinkWatchers', +} + +setting_name_replacement: dict[str, str] = { + 'x': 'eapol', +} + + +def must_replace_name(name: str) -> bool: + return (keyword.iskeyword(name) + or keyword.issoftkeyword(name) + or hasattr(builtins, name)) + + +class NmSettingPropertyIntrospection: + def __init__(self, name: str, + description: str, + name_upper: str, + dbus_type: str, + parent: NmSettingsIntrospection, + default: Optional[str] = None, + ) -> None: + self.name = name + self.description = description + self.name_upper = name_upper + self.python_name = name_upper.lower() + self.dbus_type = dbus_type + self.default = default + + if must_replace_name(self.python_name): + self.python_name = (f"{parent.name_upper.lower()}" + f"_{self.python_name}") + + extra_typing = dbus_to_python_extra_typing_imports.get(dbus_type) + if extra_typing is not None: + parent.properties_want_imports.update(extra_typing) + + @cached_property + def python_type(self) -> str: + if self.dbus_type == 'aa{sv}': + return f"List[{array_of_vardicts_python_classes[self.name]}]" + + return dbus_to_python_type_map[self.dbus_type] + + @cached_property + def python_inner_class(self) -> Optional[str]: + return array_of_vardicts_python_classes.get(self.name) + + +class NmSettingsIntrospection: + def __init__(self, name: str, description: str, name_upper: str, + ) -> None: + self.name = name + self.description = description + self.name_upper = name_upper + + self.properties_want_imports = {'Optional'} + self.properties: List[NmSettingPropertyIntrospection] = [] + + @cached_property + def typing_imports(self) -> Set[str]: + typing_imports: Set[str] = self.properties_want_imports.copy() + + return typing_imports + + @cached_property + def python_class_name(self) -> str: + + camel_case = ''.join( + map(str.title, self.snake_name.split('_')) + ) + + return camel_case + 'Settings' + + @cached_property + def snake_name(self) -> str: + underscore_name = self.name.replace('-', '_') + + no_first_digits_name = ''.join( + dropwhile( + lambda s: not str.isalpha(s), underscore_name)) + + return setting_name_replacement.get( + no_first_digits_name, + no_first_digits_name, + ) + + @cached_property + def datatypes_imports(self) -> list[str]: + datatypes_found: list[str] = [] + for x in self.properties: + if (datatype := x.python_inner_class) is not None: + datatypes_found.append(datatype) + + return datatypes_found + + @cached_property + def is_optional_setting(self) -> bool: + if self.name == 'connection': + return False + + return True + + @cached_property + def secret_fields(self) -> Set[str]: + all_fields: Set[str] = set() + possible_secret_fields: Set[str] = set() + + for x in self.properties: + + if (x.python_name.endswith('_flags') + and x.python_type == 'int' + and x.description.startswith('Flags indicating')): + possible_secret_fields.add( + x.python_name.removesuffix('_flags')) + else: + all_fields.add(x.python_name) + + return all_fields.intersection(possible_secret_fields) + + +def extract_docbook_paragraphs(docbook_node: Element) -> List[str]: + return [x.text for x in docbook_node] + + +def extract_description_paragraph(description_node: Element) -> str: + return description_node.text + + +def extract_and_format_option_description(node: Element) -> str: + formatted_paragraphs: list[str] = [] + paragraphs: list[str] = [] + description = 'Not documented' + + for doc_node in node: + if doc_node.tag == 'description-docbook': + paragraphs.extend(extract_docbook_paragraphs(doc_node)) + elif doc_node.tag == 'description': + description = extract_description_paragraph(doc_node) + elif doc_node.tag == 'deprecated': + ... + elif doc_node.tag == 'deprecated-docbook': + ... + else: + raise ValueError("Unknown doc node", doc_node.tag) + + if paragraphs: + first_para = paragraphs.pop(0) + else: + first_para = description + + formatted_paragraphs.append( + fill( + first_para, + width=72, + subsequent_indent=' ', + ) + ) + for para in paragraphs: + formatted_paragraphs.append( + fill( + para, + width=72, + initial_indent=' ', + subsequent_indent=' ', + ) + ) + + return '\n\n'.join(formatted_paragraphs) + + +def convert_property(node: Element, + parent: NmSettingsIntrospection + ) -> Optional[NmSettingPropertyIntrospection]: + options = node.attrib.copy() + + try: + unconverted_type = options.pop('type') + except KeyError: + return None + + try: + dbus_type = dbus_name_type_map[unconverted_type] + except KeyError: + dbus_type = dbus_name_type_map[unconverted_type.split('(')[1][:-1]] + + options['dbus_type'] = dbus_type + options['description'] = extract_and_format_option_description(node) + + new_property = NmSettingPropertyIntrospection(**options, parent=parent) + + try: + new_property.python_type + except KeyError: + return None + + return new_property + + +def generate_introspection(root: Element) -> List[NmSettingsIntrospection]: + settings_introspection: List[NmSettingsIntrospection] = [] + for setting_node in root: + setting = NmSettingsIntrospection(**setting_node.attrib) + + for x in setting_node: + new_property = convert_property(x, setting) + if new_property is not None: + setting.properties.append(new_property) + + settings_introspection.append(setting) + + return settings_introspection + + +def main( + settings_xml_path: Path, + settings_regex_filter: Optional[Pattern] = None, +) -> None: + jinja_env = Environment( + loader=FileSystemLoader(Path('./tools/jinja_templates/')), + undefined=StrictUndefined, + ) + settings_template = jinja_env.get_template('setting.py.jinja2') + + tree = parse(settings_xml_path) + all_settings = sorted( + generate_introspection(tree.getroot()), + key=lambda x: x.snake_name, + ) + + settings_dir = Path('./sdbus_async/networkmanager/settings/') + for setting in all_settings: + + if settings_regex_filter is not None: + if not settings_regex_filter.match(setting.snake_name): + continue + + setting_py_file = settings_dir / (setting.snake_name + '.py') + with open(setting_py_file, mode='w') as f: + f.write(settings_template.render(setting=setting)) + + profile_template = jinja_env.get_template('profile.py.jinja2') + with open(settings_dir / 'profile.py', mode='w') as f: + f.write(profile_template.render(all_settings=all_settings)) + + init_template = jinja_env.get_template('__init__.py.jinja2') + with open(settings_dir / '__init__.py', mode='w') as f: + f.write(init_template.render(all_settings=all_settings)) + + docs_template = jinja_env.get_template('profile_settings.rst.jinja2') + with open('./docs/profile_settings.rst', mode='w') as f: + f.write(docs_template.render(all_settings=all_settings)) + + +if __name__ == '__main__': + arg_parser = ArgumentParser() + arg_parser.add_argument( + 'settings_xml_path', + type=Path, + default=Path('./nm-settings-docs-dbus.xml'), + ) + arg_parser.add_argument( + '--settings-regex-filter', + type=regex_compile, + ) + + args = arg_parser.parse_args() + + main(**vars(args)) diff --git a/tools/generate-settings-dataclasses.py b/tools/generate-settings-dataclasses.py index 90022a5..d770f76 100755 --- a/tools/generate-settings-dataclasses.py +++ b/tools/generate-settings-dataclasses.py @@ -1,18 +1,14 @@ #!/usr/bin/env python +# Based on NetworkManager-1.39.2/tools/generate-docs-nm-settings-docs-merge.py # SPDX-License-Identifier: LGPL-2.1-or-later import collections -from typing import Any, Dict, List, OrderedDict, Optional, Tuple -import xml.etree.ElementTree as ElementTree +import io +import os import textwrap - - -def dbg(msg: Any) -> None: - print(f"{msg}") - - -def write(msg: Any) -> None: - print(f"{msg}") - +import xml.etree.ElementTree as ElementTree +from argparse import ArgumentParser +from pathlib import Path +from typing import Any, Dict, List, Optional, OrderedDict, Tuple dbus_type_name_map = { "b": "bool", @@ -55,6 +51,7 @@ def write(msg: Any) -> None: ############################################################################### +# Order of the connection types(settings_classes) in the profile dataclass: _setting_name_order = [ "connection", "ipv4", @@ -110,6 +107,7 @@ def write(msg: Any) -> None: "wpan", ] +# List of modules for which we must generate an import of typing.List list_modules = [ "bridge", "bridge_port", @@ -132,8 +130,30 @@ def write(msg: Any) -> None: "wireless_security", ] +# Order of special properties which must be first because they are not optional +_property_name_order = [ + "connection.id", + "connection.type", + "connection.uuid", +] + + +def _property_name_order_idx(name: str) -> int: + """Return the sort index for the given connection setting property""" + try: + return _property_name_order.index(name) + except ValueError: + # Properties not in _property_name_order are sorted last and then by their name + return len(_property_name_order) + + +def key_fcn_property_name(n1: str) -> Tuple[int, str]: + """key function for sorted(), used to sort connection properties""" + return (_property_name_order_idx(n1), n1) + def _setting_name_order_idx(name: str) -> int: + """Return the sort index for the given connection type(settings_class)""" try: return _setting_name_order.index(name) except ValueError: @@ -141,13 +161,20 @@ def _setting_name_order_idx(name: str) -> int: def key_fcn_setting_name(n1: str) -> Tuple[int, str]: + """key function for sorted(), used to sort the settings_classes(connection types)""" return (_setting_name_order_idx(n1), n1) def iter_keys_of_dicts( - dicts: List[Dict[str, Any]], key: Optional[Any] = None + dicts: List[Dict[str, Any]], key: Optional[Any] = None, prefix: Optional[str] = "" ) -> List[str]: - keys = {k for d in dicts for k in d.keys()} + """Return a sorted list of settings_classes or connection properties + + To support sorting the required properties of settings_classes first, + the settingsname can be passed ad prefix argument to let the key function + return the correct sort index for the given settingsname property. + """ + keys = {f'{prefix}{k}' for d in dicts for k in d.keys()} return sorted(keys, key=key) @@ -171,9 +198,7 @@ def node_get_attr(nodes: List[Optional[Any]], name: str) -> Any: return None -def node_set_attr( - dst_node: Any, name: str, nodes: List[Optional[Any]] -) -> None: +def node_set_attr(dst_node: Any, name: str, nodes: List[Optional[Any]]) -> None: x = node_get_attr(nodes, name) if x: dst_node.set(name, x) @@ -183,158 +208,213 @@ def find_first_not_none(itr: List[Any]) -> Optional[Any]: return next((i for i in itr if i is not None), None) -############################################################################### -gl_input_files = ["man/nm-settings-docs-dbus.xml"] - -xml_roots = [ElementTree.parse(f).getroot() for f in gl_input_files] -assert all(root.tag == "nm-setting-docs" for root in xml_roots) -settings_roots = [node_to_dict(root, "setting", "name") for root in xml_roots] - -root_node = ElementTree.Element("nm-setting-docs") -# print("") -license = "SPDX-License-Identifier: LGPL-2.1-or-later" -script = "This file was generated by tools/generate-settings-dataclasses.py" -header = f"""# {license}\n# {script}, -# if possible, please make changes by also updating the script. -""" -i = open("sdbus_async/networkmanager/settings/__init__.py", mode="w") -p = open("sdbus_async/networkmanager/settings/profile.py", mode="r") -profile_py = open("sdbus_async/networkmanager/settings/profile.py").read() -start_string = "# start of the generated list of settings classes\n" -start_index = profile_py.index(start_string) + len(start_string) -# end_string = " def to_dbus" -end_string = " # end of the generated list of settings classes\n" -end_index = profile_py.index(end_string) -p = open("sdbus_async/networkmanager/settings/profile.py", mode="w") -i.write(header) -p.write(profile_py[:start_index]) -classes = [] -for settingname in iter_keys_of_dicts(settings_roots, key_fcn_setting_name): - settings = [d.get(settingname) for d in settings_roots] - properties = [node_to_dict(s, "property", "name") for s in settings] - if properties == [OrderedDict()]: - continue - if settingname in ["tc", "sriov"]: - continue # Not supported by this codegen yet(needs Qdiscs and vfs) - module = settingname.replace('-', '_') - for prefix in ["6", "802_11_", "802_3_"]: - if module.startswith(prefix): - module = module.replace(prefix, "") - break - if settingname.startswith("802-1x"): - module = f"ieee{module}" - temp = module.split('_') + ["Settings"] - classname = temp[0].title() + ''.join(ele.title() for ele in temp[1:]) - if classname.startswith("Ieee"): - classname.replace("Ieee", "IEEE") - i.write(f"from .{module} import {classname}\n") - classes.append(classname) - f = open(f"sdbus_async/networkmanager/settings/{module}.py", mode="w") - f.write(header) - f.write("from __future__ import annotations\n") - f.write("from dataclasses import dataclass, field\n") - f.write("from typing import") - if module in ["bond", "ethernet", "ovs_external_ids", "vpn", "user"]: - f.write(" Dict,") - if module in list_modules: - f.write(" List,") - f.write(" Optional\n") - f.write("from .base import NetworkManagerSettingsMixin\n") - if settingname.startswith("ipv"): - f.write("from .datatypes import AddressData, RouteData\n") - if settingname.startswith("bridge"): - f.write("from .datatypes import Vlans\n") - if settingname.startswith("team"): - f.write("from .datatypes import LinkWatchers\n") - if settingname == "wireguard": - f.write("from .datatypes import WireguardPeers as Peers\n") - f.write("\n\n") - - setting_node = ElementTree.SubElement(root_node, "setting") - if module != "connection": - p.write(f" {module}: Optional[{classname}] = field(\n") - p.write(f" metadata={{'dbus_name': '{settingname}',\n") - p.write(f" 'settings_class': {classname}}},\n") - p.write(" default=None,\n") - p.write(" )\n") - f.write("@dataclass\n") - f.write(f"class {classname}(NetworkManagerSettingsMixin):\n") - setting_node.set("name", settingname) - desc = node_get_attr(settings, "description") - f.write(' """' + desc + '"""\n\n') - # node_set_attr(setting_node, "alias", settings) - for property_name in iter_keys_of_dicts(properties): - properties_attrs = [p.get(property_name) for p in properties] - property_node = ElementTree.SubElement(setting_node, "property") - property_node.set("name", property_name) - t = node_get_attr(properties_attrs, "type") - attribute = property_name.replace('-', '_') - for builtin in ["id", "type"]: - if attribute == builtin: - attribute = f"{module}_{attribute}" - - if not t: - t = "string" - if t.startswith("array of legacy"): +# Generate docs/options.rst, see: +# https://github.com/python-sdbus/python-sdbus-networkmanager/pull/39#issuecomment-1186522147 +def open_profile_settings_rst() -> io.TextIOWrapper: + profile_settings_rst = open("docs/profile_settings.rst", "w") + profile_settings_rst.write("Connection Profile Settings Helpers\n") + profile_settings_rst.write("===================================\n\n") + return profile_settings_rst + + +def append_sphinx_autoclass(profile_settings: io.TextIOWrapper, classname: str) -> None: + classpath = f"sdbus_async.networkmanager.settings.{classname}" + profile_settings.write(f"\n.. autoclass:: {classpath}\n :members:\n") + + +# The code quality of this function is poor(Sourcery says 5%), needs refactoring, +# also see the rework in tools/generate-settings-dataclasses-jinja.py +def main(settings_xml_path: Path) -> None: + profile_settings_rst = open_profile_settings_rst() + gl_input_files = [settings_xml_path] + + xml_roots = [ElementTree.parse(f).getroot() for f in gl_input_files] + assert all(root.tag == "nm-setting-docs" for root in xml_roots) + settings_roots = [node_to_dict(root, "setting", "name") for root in xml_roots] + + # Generate the file header + license = "SPDX-License-Identifier: LGPL-2.1-or-later" + script = "This file was generated by tools/generate-settings-dataclasses.py" + header = f"""# {license}\n# {script}, +# if possible, please make changes by also updating the script.\n""" + i = open("sdbus_async/networkmanager/settings/__init__.py", mode="w") + profile_path = "sdbus_async/networkmanager/settings/profile.py" + profile_py = open(profile_path).read() + + # define start and end markers for generating part of settings/profile.py: + start_string = "# start of the generated list of settings classes\n" + start_index = profile_py.index(start_string) + len(start_string) + end_string = " # end of the generated list of settings classes\n" + end_index = profile_py.index(end_string) + profile_path_new = f"{profile_path}.new" + p = open(profile_path_new, mode="w") + + # write the file headers + i.write(header) + p.write(profile_py[:start_index]) + classes = [] + + # generate the connection type settings classes: + for settingname in iter_keys_of_dicts(settings_roots, key_fcn_setting_name): + settings = [d.get(settingname) for d in settings_roots] + properties = [node_to_dict(s, "property", "name") for s in settings] + if properties == [OrderedDict()]: continue - if t not in dbus_name_type_map: - print(f"{settingname}.{property_name}: type '{t}' not found") - ty = t.replace(")", "")[-5:] - if ty in dbus_name_type_map: - t = ty - if t in ["{sv}'"]: - t = "{sv}" - dbustype = dbus_name_type_map[t] - if dbustype == "aa{sv}": - default = node_get_attr(properties_attrs, "default") - inner_cls = ( - property_name.title().replace("-", "").replace("data", "Data") - ) - f.write(f" {attribute}: Optional[List[{inner_cls}]] = field(\n") - f.write(f" metadata={{'dbus_name': '{property_name}',\n") - f.write(f" 'dbus_type': '{dbustype}',\n") - f.write(f" 'dbus_inner_class': {inner_cls}}},\n") - f.write(f" default={str(default).title()},\n )\n") - else: - attribute_type = dbus_type_name_map[dbustype] - optional = module != "bond" - if optional: - f.write(f" {attribute}: Optional[{attribute_type}]") + if settingname in ["tc", "sriov"]: + continue # Not supported by this codegen yet(needs Qdiscs and vfs) + module = settingname.replace('-', '_') + for prefix in ["6", "802_11_", "802_3_"]: + if module.startswith(prefix): + module = module.replace(prefix, "") + break + if settingname.startswith("802-1x"): + module = f"ieee{module}" + temp = module.split('_') + ["Settings"] + classname = temp[0].title() + ''.join(ele.title() for ele in temp[1:]) + if classname.startswith("Ieee"): + classname.replace("Ieee", "IEEE") + i.write(f"from .{module} import {classname}\n") + classes.append(classname) + f = open(f"sdbus_async/networkmanager/settings/{module}.py", mode="w") + + # Generate module type headers with the needed import statements + f.write(header) + f.write("from __future__ import annotations\n") + f.write("from dataclasses import dataclass, field\n") + f.write("from typing import") + if module in ["bond", "ethernet", "ovs_external_ids", "vpn", "user"]: + f.write(" Dict,") + if module in list_modules: + f.write(" List,") + f.write(" Optional\n") + f.write("from .base import NetworkManagerSettingsMixin\n") + if settingname.startswith("ipv"): + f.write("from .datatypes import AddressData, RouteData\n") + if settingname.startswith("bridge"): + f.write("from .datatypes import Vlans\n") + if settingname.startswith("team"): + f.write("from .datatypes import LinkWatchers\n") + if settingname == "wireguard": + f.write("from .datatypes import WireguardPeers as Peers\n") + f.write("\n\n") + + # Generate the settings_class and it's entry in profile.py: + if module != "connection": + p.write(f" {module}: Optional[{classname}] = field(\n") + p.write(f" metadata={{'dbus_name': '{settingname}',\n") + p.write(f" 'settings_class': {classname}}},\n") + p.write(" default=None,\n") + p.write(" )\n") + f.write("@dataclass\n") + f.write(f"class {classname}(NetworkManagerSettingsMixin):\n") + append_sphinx_autoclass(profile_settings_rst, classname) + + # generate the docstring of the new settings_class + desc = node_get_attr(settings, "description") + f.write(' """' + desc + '"""\n\n') + + # Generate the attributes of the settings_class for this profile type: + + for property in iter_keys_of_dicts( + properties, key_fcn_property_name, f'{settingname}.' + ): + property_name = property[len(settingname) + 1 :] + properties_attrs = [p.get(property_name) for p in properties] + t = node_get_attr(properties_attrs, "type") + attribute = property_name.replace('-', '_') + for builtin in ["id", "type"]: + if attribute == builtin: + attribute = f"{module}_{attribute}" + + if not t: + t = "string" + if t.startswith("array of legacy"): + continue + if t not in dbus_name_type_map: + print(f"{settingname}.{property_name}: type '{t}' not found") + ty = t.replace(")", "")[-5:] + if ty in dbus_name_type_map: + t = ty + if t in ["{sv}'"]: + t = "{sv}" + dbustype = dbus_name_type_map[t] + if dbustype == "aa{sv}": + default = node_get_attr(properties_attrs, "default") + inner_cls = ( + property_name.title().replace("-", "").replace("data", "Data") + ) + f.write(f" {attribute}: Optional[List[{inner_cls}]] = field(\n") + f.write(f" metadata={{'dbus_name': '{property_name}',\n") + f.write(f" 'dbus_type': '{dbustype}',\n") + f.write(f" 'dbus_inner_class': {inner_cls}}},\n") + f.write(f" default={str(default).title()},\n )\n") else: - f.write(f" {attribute}: {attribute_type}") - f.write(" = field(\n") - meta = f"'dbus_name': '{property_name}', 'dbus_type':@'{dbustype}'" - line = "metadata={" + meta + "}," - wrapper = textwrap.TextWrapper( - width=80, - initial_indent=" ", - subsequent_indent=" ", - ) - lines = wrapper.wrap(text=line) - for line in lines: - f.write(line.replace(":@", ": ") + '\n') - default = node_get_attr(properties_attrs, "default") - if default in ["{}", "0", "-1"]: - default = "None" - if optional: - f.write(f" default={str(default).title()},\n") - f.write(" )\n") - generate_descriptions_for_attributes = False - if generate_descriptions_for_attributes: - desc = node_get_attr(properties_attrs, "description") + attribute_type = dbus_type_name_map[dbustype] + optional = property not in _property_name_order + if optional: + f.write(f" {attribute}: Optional[{attribute_type}]") + else: + f.write(f" {attribute}: {attribute_type}") + f.write(" = field(\n") + meta = f"'dbus_name': '{property_name}', " f"'dbus_type':@'{dbustype}'" + line = "metadata={" + meta + "}," wrapper = textwrap.TextWrapper( - width=74, initial_indent=" ", subsequent_indent=" " + width=80, + initial_indent=" ", + subsequent_indent=" ", ) - lines = wrapper.wrap(text=f'"""{desc}') - if len(lines) == 1: - print(lines[0] + '"""') - else: - for line in lines: - f.write(line) - f.write(' """') - f.write("") -i.write('\n__all__ = (\n') -for cls in classes: - i.write(f" '{cls}',\n") -i.write(")\n") -p.write(profile_py[end_index:]) + lines = wrapper.wrap(text=line) + for line in lines: + f.write(line.replace(":@", ": ") + '\n') + default = node_get_attr(properties_attrs, "default") + if default in ["{}", "0", "-1"]: + default = None + if optional: + if dbustype == "a{ss}" and default: + default = f"field(default_factory = lambda: {default})" + else: + default = str(default).title() # FALSE -> False + f.write(f" default={default},\n") + f.write(" )\n") + + # Generate docstrings for attributes: Not stored by python, + # but is parsed for generating documentation and be red by + # developers when they lookup the attribute declaration: + generate_descriptions_for_attributes = True + if generate_descriptions_for_attributes: + attr_description = node_get_attr(properties_attrs, "description") + # Fix warning from sphinx: WARNING: + # Inline substitution_reference start-string without end-string + attr_docstring = attr_description.replace("|", "\|") + wrapper = textwrap.TextWrapper( + width=82, + initial_indent=" ", + subsequent_indent=" ", + ) + lines = wrapper.wrap(text=f'"""{attr_docstring}"""') + if len(lines) == 1: + print(lines[0] + '"""') + else: + for line in lines: + f.write(f'{line}\n') + # If the closing """ shall be on a new line, use: + # f.write(' """\n') + f.write("") + i.write('\n__all__ = (\n') + for cls in classes: + i.write(f" '{cls}',\n") + i.write(")\n") + p.write(profile_py[end_index:]) + os.rename(profile_path_new, profile_path) + + +if __name__ == '__main__': + arg_parser = ArgumentParser() + arg_parser.add_argument( + 'nm_settings_xml', + type=Path, + default=Path('man/nm-settings-docs-dbus.xml'), + ) + args = arg_parser.parse_args() + + main(args.nm_settings_xml) diff --git a/tools/jinja_templates/__init__.py.jinja2 b/tools/jinja_templates/__init__.py.jinja2 new file mode 100644 index 0000000..b7fc950 --- /dev/null +++ b/tools/jinja_templates/__init__.py.jinja2 @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations + +from .profile import ConnectionProfile +{% for setting in all_settings -%} +from .{{ setting.snake_name }} import {{ setting.python_class_name }} +{% endfor %} +from .datatypes import ( + AddressData, + RouteData, + LinkWatchers, + Vlans, + WireguardPeers, + RoutingRules, + Vfs, +) + +__all__ = ( + 'ConnectionProfile', +{%- for setting in all_settings %} + '{{ setting.python_class_name }}', +{%- endfor %} + + 'AddressData', + 'RouteData', + 'LinkWatchers', + 'Vlans', + 'WireguardPeers', + 'RoutingRules', + 'Vfs', +) + diff --git a/tools/jinja_templates/profile.py.jinja2 b/tools/jinja_templates/profile.py.jinja2 new file mode 100644 index 0000000..22b0ddd --- /dev/null +++ b/tools/jinja_templates/profile.py.jinja2 @@ -0,0 +1,208 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations + +from dataclasses import dataclass, field, fields +from typing import Any, Dict, Generator, Optional + +from .base import NetworkManagerSettingsMixin +{% for setting in all_settings -%} +from .{{ setting.snake_name }} import {{ setting.python_class_name }} +{% endfor -%} +from ..types import NetworkManagerConnectionProperties, SettingsDict + + +@dataclass +class ConnectionProfile: + """ + Connection profiles + ------------------- + + NetworkManager is based on a concept of connection profiles, most often + referred to just as "connections". Connection profiles provide a network + configuration. When NetworkManager activates a connection profile on a + network device, the configuration will be applied and an active network + connection will be established. Users are free to create as many + connection profiles as they see fit. Thus they are flexible in having + various network configurations for different networking needs: + https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/nm-settings-dbus.html + + Connection profiles are handled by NetworkManager via a settings service + and are exported on D-Bus (/org/freedesktop/NetworkManager/Settings/) + + Definition of a connection profile: + A specific, encapsulated, independent group of settings describing + all the configuration required to connect to a specific network. + + It is referred to by a unique identifier called the UUID. + A connection profile is tied to a one specific device type, + but not necessarily a specific hardware device. + + A connection profile is composed of one or more Settings objects. + + Settings objects + ---------------- + + A group of related key/value pairs describing a specific piece + of a Connection (profile). Keys are also referred to as properties. + """ + # The list of the settings classes was generated by + # tools/generate-settings-dataclasses-jinja.py which generates the + # settings classes themselves as well. + # If possible, please make changes by also updating the script. + + # start of the generated list of settings classes +{%- for setting in all_settings %} + {{ setting.snake_name }}: {%- if setting.is_optional_setting %} Optional[{{ setting.python_class_name }}] {% else %} {{ setting.python_class_name }} {% endif -%} = field( + metadata={'dbus_name': '{{ setting.name }}', + 'settings_class': {{ setting.python_class_name }}}, +{%- if setting.is_optional_setting %} + default=None, +{%- else %} + default_factory={{ setting.python_class_name }}, +{%- endif %} + ) +{%- endfor %} + # end of the generated list of settings classes + + def to_dbus(self) -> NetworkManagerConnectionProperties: + new_dict: NetworkManagerConnectionProperties = {} + + for x in fields(self): + value = getattr(self, x.name) + if value is None: + continue + + new_dict[x.metadata['dbus_name']] = value.to_dbus() + + return new_dict + + def to_settings_dict(self, defaults: bool = False) -> SettingsDict: + """Return a simple dictionary using the same key names like the dbus + dict from to_dbus(), but without the dbus signatures returned by it. + + Contrary to dataclasses.asdict(), it provides the original dbus keys, + e.g. with numerical prefixes like "802-11-", dashes, and "id"/"type". + + The key names provided are exactly as documented in these tables: + https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html + + param defaults: Whether properties with default values are returned. + """ + new_dict = {} + for x in fields(self): + settings_class = getattr(self, x.name) + if settings_class: + settingsdomain_dict = settings_class.to_settings_dict(defaults) + if settingsdomain_dict != {}: + new_dict[x.metadata['dbus_name']] = settingsdomain_dict + return new_dict + + @property + def dbus_name_to_settings_class(self) -> Dict[str, str]: + return {f.metadata['dbus_name']: f.name + for f in fields(self)} + + @classmethod + def from_dbus(cls, dbus_dict: NetworkManagerConnectionProperties + ) -> ConnectionProfile: + for domain in ("ipv4", "ipv6"): + group = dbus_dict.get(domain, None) + if group: + for key in ("addresses", "routes"): + group.pop(key, None) + + unvarianted_options: Dict[str, Any] = {} + for k, v in dbus_dict.items(): + try: + unvarianted_options[SETTING_DBUS_NAME_TO_NAME[k]] = ( + SETTING_TO_CLASS[k].from_dbus(v) + ) + except KeyError: + ... + + return cls(**unvarianted_options) + + @classmethod + def from_settings_dict( + cls, settings_dict: SettingsDict + ) -> ConnectionProfile: + """Return a ConnectionProfile created from a simple settings dict + A simple settings dict uses the same keys as from_dbus() and to_dbus() + but without the dbus variable signatures used by NetworkManader.py + + This means a simple settings dict does not use the underscore in keys + like the attributes of this class have to use and use "id" and "type". + """ + unvarianted_options: Dict[str, Any] = { + SETTING_DBUS_NAME_TO_NAME[k]: SETTING_TO_CLASS[k].from_dict(v) + for k, v in settings_dict.items()} + return cls(**unvarianted_options) + + def update(self, other: ConnectionProfile) -> None: + """Update this connection profile with the settings from the other. + + Similar to dict.update method. + """ + for f in fields(other): + settings_field_name = f.name + other_settings = getattr(other, settings_field_name) + + if other_settings is None: + continue + + my_settings = getattr(self, settings_field_name) + + if my_settings is None: + setattr(self, settings_field_name, other_settings) + continue + + for setting_field in fields(other_settings): + setting_field_name = setting_field.name + + other_setting = getattr(other_settings, setting_field_name) + + if other_setting is None: + continue + + setattr(my_settings, setting_field_name, other_setting) + + def update_secrets_generator( + self) -> Generator[str, ConnectionProfile, None]: + for attr_name, value in vars(self).items(): + if value is None: + continue + + if not isinstance(value, NetworkManagerSettingsMixin): + continue + + secret_setting_name = value.secret_name + + if not secret_setting_name: + continue + + secret_profile = yield secret_setting_name + current_setting_secrets = getattr(secret_profile, attr_name) + + for secret_attribute in value.secret_fields_names: + setattr( + value, + secret_attribute, + getattr( + current_setting_secrets, + secret_attribute, + ), + ) + + +SETTING_DBUS_NAME_TO_NAME: Dict[str, str] = { + f.metadata['dbus_name']: f.name + for f in fields(ConnectionProfile) +} + +SETTING_TO_CLASS: Dict[str, NetworkManagerSettingsMixin] = { + f.metadata['dbus_name']: f.metadata['settings_class'] + for f in fields(ConnectionProfile) +} + diff --git a/tools/jinja_templates/profile_settings.rst.jinja2 b/tools/jinja_templates/profile_settings.rst.jinja2 new file mode 100644 index 0000000..b545b36 --- /dev/null +++ b/tools/jinja_templates/profile_settings.rst.jinja2 @@ -0,0 +1,8 @@ +Connection Profile Settings Helpers +=================================== + +{% for setting in all_settings -%} +.. autoclass:: sdbus_async.networkmanager.settings.{{ setting.python_class_name }} + :members: + +{% endfor %} diff --git a/tools/jinja_templates/setting.py.jinja2 b/tools/jinja_templates/setting.py.jinja2 new file mode 100644 index 0000000..ec9a4f2 --- /dev/null +++ b/tools/jinja_templates/setting.py.jinja2 @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file was generated by tools/generate-settings-dataclasses-jinja.py, +# if possible, please make changes by also updating the script. +from __future__ import annotations +from dataclasses import dataclass, field +from typing import {{ setting.typing_imports|sort|join(', ') }} +from .base import NetworkManagerSettingsMixin +{% if setting.datatypes_imports -%} +from .datatypes import {{ setting.datatypes_imports|sort|join(', ') }} +{% endif %} + +@dataclass +class {{ setting.python_class_name }}(NetworkManagerSettingsMixin): + """{{ setting.description }}""" +{%- if setting.secret_fields %} + secret_fields_names = ['{{ setting.secret_fields|sort|join("', '") }}'] + secret_name = '{{ setting.name }}' +{%- endif %} +{% for property in setting.properties %} + {{ property.python_name }}: Optional[{{ property.python_type }}] = field( + metadata={ + 'dbus_name': '{{ property.name }}', + 'dbus_type': '{{ property.dbus_type }}', +{%- if property.python_inner_class %} + 'dbus_inner_class': {{ property.python_inner_class }}, +{%- endif %} + }, + default=None, + ) + """{{property.description}}"""{% endfor %} +