Skip to content

Commit 86f78a0

Browse files
author
Bernhard Kaindl
committed
enums.py: Fix ConnectionType and show it's use.
ConnectionType gives a 1:1 map from a DeviceType.name to a profile.connection.connection_type value, which can be used to find usable connections for a given DeviceType. Fix ConnectionType (added very recently, not yet released) to not use ConnectionType.WIRED for an Ethernet connection but ConnectionType.ETHERNET, which matches DeviceType.ETHERNET. Since ConnectionType is new, the fix is safe to apply now. Demonstrate it's use in the netdevinfo examples like shown in PR #36 with conn_type = getattr(ConnectionType, dev_type).
1 parent ff50bb4 commit 86f78a0

File tree

3 files changed

+128
-21
lines changed

3 files changed

+128
-21
lines changed

examples/async/netdevinfo-async.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,57 @@
1010
import asyncio
1111
import sdbus
1212
from sdbus_async.networkmanager import (
13+
ConnectionType,
14+
NetworkConnectionSettings,
1315
NetworkManager,
16+
NetworkManagerSettings,
1417
NetworkDeviceGeneric,
1518
IPv4Config,
1619
DeviceType,
1720
NetworkDeviceWireless,
1821
WiFiOperationMode,
1922
AccessPoint,
2023
)
21-
from typing import Any, Dict, List, Tuple
24+
from typing import Any, Dict, List, Optional, Tuple
2225
NetworkManagerAddressData = List[Dict[str, Tuple[str, Any]]]
2326

2427

28+
async def get_most_recent_connection_id(ifname, dev_type) -> Optional[str]:
29+
"""Return the most-recently used connection_id for this device
30+
31+
Besides getting the currently active connection, this will succeed
32+
in getting the most recent connection when a device is not connected
33+
at the moment this function is executed.
34+
35+
It uses getattr(ConnectionType, dev_type) to get the connection_type
36+
used for connection_profiles for this DeviceType.
37+
38+
With a slight modification, this could return the most recent connections
39+
of the given device, ordered by the time of the last use of them.
40+
"""
41+
settings_service = NetworkManagerSettings()
42+
connection_paths: List[str] = await settings_service.connections
43+
conns = {}
44+
for connection_path in connection_paths:
45+
connection_manager = NetworkConnectionSettings(connection_path)
46+
connection = (await connection_manager.connection_profile()).connection
47+
# Filter connection profiles matching the connection type for the device:
48+
if connection.connection_type != getattr(ConnectionType, dev_type):
49+
continue
50+
# If the interface_name of a connection profiles is set, it must match:
51+
if connection.interface_name and connection.interface_name != ifname:
52+
continue
53+
# If connection.timestamp is not set, it was never active. Set it to 0:
54+
if not connection.timestamp:
55+
connection.timestamp = 0
56+
# Record the connection_ids of the matches, and timestamp is the key:
57+
conns[connection.timestamp] = connection.connection_id
58+
if not len(conns):
59+
return None
60+
# Returns the connection_id of the highest timestamp which was found:
61+
return conns.get(max(conns.keys()))
62+
63+
2564
async def list_networkdevice_details_async() -> None:
2665
nm = NetworkManager()
2766
devices_paths = await nm.get_devices()
@@ -31,11 +70,19 @@ async def list_networkdevice_details_async() -> None:
3170
device_ip4_conf_path: str = await generic_device.ip4_config
3271
if device_ip4_conf_path == "/":
3372
continue
73+
if not await generic_device.managed:
74+
continue
75+
dev_type = DeviceType(await generic_device.device_type).name
76+
if dev_type == DeviceType.BRIDGE.name:
77+
continue
3478

79+
dev_name = await generic_device.interface
3580
ip4_conf = IPv4Config(device_ip4_conf_path)
3681
gateway: str = await ip4_conf.gateway
3782

38-
print("Device: ", await generic_device.interface)
83+
print("Type: ", dev_type.title())
84+
print("Name: ", dev_name)
85+
3986
if gateway:
4087
print("Gateway:", gateway)
4188

@@ -47,7 +94,7 @@ async def list_networkdevice_details_async() -> None:
4794
for dns in nameservers:
4895
print("DNS: ", dns["address"][1])
4996

50-
if await generic_device.device_type == DeviceType.WIFI:
97+
if dev_type == DeviceType.WIFI.name:
5198
wifi = NetworkDeviceWireless(device_path)
5299
print("Wifi: ", WiFiOperationMode(await wifi.mode).name.title())
53100
ap = AccessPoint(await wifi.active_access_point)
@@ -56,6 +103,9 @@ async def list_networkdevice_details_async() -> None:
56103
print("SSID: ", ssid.decode("utf-8", "ignore"))
57104
if await ap.strength:
58105
print("Signal: ", await ap.strength)
106+
connection_id = await get_most_recent_connection_id(dev_name, dev_type)
107+
if connection_id:
108+
print("Profile:", connection_id)
59109

60110
print("")
61111

examples/block/netdevinfo.py

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,100 @@
99
# https://networkmanager.dev/docs/api/latest/ref-dbus-devices.html
1010
import sdbus
1111
from sdbus_block.networkmanager import (
12+
ConnectionType,
13+
NetworkConnectionSettings,
1214
NetworkManager,
15+
NetworkManagerSettings,
1316
NetworkDeviceGeneric,
1417
IPv4Config,
1518
DeviceType,
1619
NetworkDeviceWireless,
1720
WiFiOperationMode,
1821
AccessPoint,
1922
)
23+
from typing import Any, Dict, List, Optional, Tuple
24+
NetworkManagerAddressData = List[Dict[str, Tuple[str, Any]]]
25+
26+
27+
def get_most_recent_connection_id(ifname, dev_type) -> Optional[str]:
28+
"""Return the most-recently used connection_id for this device
29+
30+
Besides getting the currently active connection, this will succeed
31+
in getting the most recent connection when a device is not connected
32+
at the moment this function is executed.
33+
34+
It uses getattr(ConnectionType, dev_type) to get the connection_type
35+
used for connection_profiles for this DeviceType.
36+
37+
With a slight modification, this could return the most recent connections
38+
of the given device, ordered by the time of the last use of them.
39+
"""
40+
settings_service = NetworkManagerSettings()
41+
connection_paths: List[str] = settings_service.connections
42+
conns = {}
43+
for connection_path in connection_paths:
44+
connection_manager = NetworkConnectionSettings(connection_path)
45+
connection = connection_manager.connection_profile().connection
46+
# Filter connection profiles matching the connection type for the device:
47+
if connection.connection_type != getattr(ConnectionType, dev_type):
48+
continue
49+
# If the interface_name of a connection profiles is set, it must match:
50+
if connection.interface_name and connection.interface_name != ifname:
51+
continue
52+
# If connection.timestamp is not set, it was never active. Set it to 0:
53+
if not connection.timestamp:
54+
connection.timestamp = 0
55+
# Record the connection_ids of the matches, and timestamp is the key:
56+
conns[connection.timestamp] = connection.connection_id
57+
if not len(conns):
58+
return None
59+
# Returns the connection_id of the highest timestamp which was found:
60+
return conns.get(max(conns.keys()))
2061

2162

2263
def list_networkdevice_details_blocking() -> None:
23-
nm = NetworkManager()
24-
devices_paths = nm.get_devices()
2564

26-
for device_path in devices_paths:
65+
for device_path in NetworkManager().get_devices():
2766
generic_device = NetworkDeviceGeneric(device_path)
28-
device_ip4_conf_path = generic_device.ip4_config
67+
device_ip4_conf_path: str = generic_device.ip4_config
2968
if device_ip4_conf_path == "/":
3069
continue
70+
if not generic_device.managed:
71+
continue
72+
dev_type = DeviceType(generic_device.device_type).name
73+
if dev_type == DeviceType.BRIDGE.name:
74+
continue
3175

76+
dev_name = generic_device.interface
3277
ip4_conf = IPv4Config(device_ip4_conf_path)
78+
gateway: str = ip4_conf.gateway
79+
80+
print("Type: ", dev_type.title())
81+
print("Name: ", dev_name)
3382

34-
print("Device: ", generic_device.interface)
35-
if ip4_conf.gateway:
36-
print("Gateway:", ip4_conf.gateway)
83+
if gateway:
84+
print("Gateway:", gateway)
3785

38-
for ip4addr in ip4_conf.address_data:
39-
print(f'Address: {ip4addr["address"][1]}/{ip4addr["prefix"][1]}')
86+
address_data: NetworkManagerAddressData = ip4_conf.address_data
87+
for inetaddr in address_data:
88+
print(f'Address: {inetaddr["address"][1]}/{inetaddr["prefix"][1]}')
4089

41-
for dns in ip4_conf.nameserver_data:
90+
nameservers: NetworkManagerAddressData = ip4_conf.nameserver_data
91+
for dns in nameservers:
4292
print("DNS: ", dns["address"][1])
4393

44-
if generic_device.device_type == DeviceType.WIFI:
45-
wifidevice = NetworkDeviceWireless(device_path)
46-
print("Wifi: ", WiFiOperationMode(wifidevice.mode).name.title())
47-
ap = AccessPoint(wifidevice.active_access_point)
48-
if ap.ssid:
49-
print("SSID: ", ap.ssid.decode("utf-8", "ignore"))
94+
if dev_type == DeviceType.WIFI.name:
95+
wifi = NetworkDeviceWireless(device_path)
96+
print("Wifi: ", WiFiOperationMode(wifi.mode).name.title())
97+
ap = AccessPoint(wifi.active_access_point)
98+
ssid: bytes = ap.ssid
99+
if ssid:
100+
print("SSID: ", ssid.decode("utf-8", "ignore"))
50101
if ap.strength:
51102
print("Signal: ", ap.strength)
103+
connection_id = get_most_recent_connection_id(dev_name, dev_type)
104+
if connection_id:
105+
print("Profile:", connection_id)
52106

53107
print("")
54108

sdbus_async/networkmanager/enums.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,9 @@ class DeviceStateReason(IntEnum):
489489
# sed 's/#define //;s/_SETTING_NAME//;s/ /=/' >.setting_defines
490490
# grep -f .connection_is_type .setting_defines |
491491
# sed 's/NM_SETTING_/ /;s/6L/SIXL/;/GENERIC/d;s/=/ = /'
492+
# Manual edit: This resulted in WIRED instead of ETHERNET, but
493+
# ETHERNET is the name used for DeviceType, so use ETHERNET instead to
494+
# be able to lookup connection profiles for Ethernet using DeviceType.
492495
#
493496
# One src/core/nm-device-*.c can support more than one ConnectionType,
494497
# thus there are more ConnectionTypes than DeviceTypes:
@@ -503,6 +506,7 @@ class ConnectionType(str, Enum):
503506
* BRIDGE
504507
* CDMA
505508
* DUMMY
509+
* ETHERNET
506510
* GSM
507511
* INFINIBAND
508512
* IP_TUNNEL
@@ -522,7 +526,6 @@ class ConnectionType(str, Enum):
522526
* VRF
523527
* VXLAN
524528
* WIFI_P2P
525-
* WIRED
526529
* WIREGUARD
527530
* WIFI
528531
* WPAN
@@ -533,6 +536,7 @@ class ConnectionType(str, Enum):
533536
BRIDGE = "bridge"
534537
CDMA = "cdma"
535538
DUMMY = "dummy"
539+
ETHERNET = "802-3-ethernet"
536540
GSM = "gsm"
537541
INFINIBAND = "infiniband"
538542
IP_TUNNEL = "ip-tunnel"
@@ -552,7 +556,6 @@ class ConnectionType(str, Enum):
552556
VRF = "vrf"
553557
VXLAN = "vxlan"
554558
WIFI_P2P = "wifi-p2p"
555-
WIRED = "802-3-ethernet"
556559
WIREGUARD = "wireguard"
557560
WIFI = "802-11-wireless"
558561
WPAN = "wpan"

0 commit comments

Comments
 (0)