gonets/SRV/services/network_service.py
2025-06-20 11:22:54 +05:00

272 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import List, Dict, Tuple, Union, Optional
from sdbus_async.networkmanager import (
NetworkManager,
NetworkDeviceGeneric,
IPv4Config,
IPv6Config,
NetworkManagerSettings,
NetworkDeviceWireless,
AccessPoint,
DeviceType,
)
from sdbus import sd_bus_open_system
from models.network import (
NetworkInterface,
IPv4Route,
IPv6Route,
IPConfig,
IndividualIPConfig,
NetworkInterfacesContainer,
WiFiDevice,
)
import subprocess
class NetworkService:
"""Сервис для взаимодействия с NetworkManager через D-Bus"""
def __init__(self):
self.system_bus = sd_bus_open_system()
self.nm = NetworkManager(self.system_bus)
self.nm_settings = NetworkManagerSettings(self.system_bus)
async def get_ip_method(self, device: NetworkDeviceGeneric,
ip_type: str) -> str:
"""Получает метод IP для устройства"""
try:
config = await device.get_applied_connection()
settings = config[0].get(ip_type, {})
return str(settings.get("method", ("s", "unknown"))[1])
except Exception:
return "unknown"
async def get_connection_name(self, device: NetworkDeviceGeneric) -> str:
"""Извлекает имя соединения (GENERAL.CONNECTION) для устройства"""
try:
applied_connection = await device.get_applied_connection()
connection_settings = applied_connection[0]
connection_info = connection_settings.get("connection", {})
return str(connection_info.get("id", ("s", "unknown"))[1])
except Exception as e:
# print(f"Ошибка при получении соединения для устройства {await device.interface}: {e}")
# срёт ошибки дл отключенных интерфейсов
# добавить потом условия для отключенных интерфейсов
return "unknown"
async def get_ip_config(self, device: NetworkDeviceGeneric,
ip_version: str) -> List[IndividualIPConfig]:
"""Получает конфигурацию IP для устройства"""
configs = []
device_ip = await getattr(device, f"ip{ip_version}_config")
if device_ip != "/":
ip_config = globals()[f"IPv{ip_version}Config"](device_ip,
self.system_bus)
address_data = await ip_config.address_data
for address in address_data:
config = IndividualIPConfig(
address=str(address["address"][1]),
routes=[
globals()[f"IPv{ip_version}Route"](
destination=route.get(
"dest",
("s",
"0.0.0.0" if ip_version == "4" else "::"))[1],
prefix=route.get("prefix", ("u", 0))[1],
next_hop=route.get(
"next-hop",
("s",
"0.0.0.0" if ip_version == "4" else "::"),
)[1],
metric=route.get("metric", ("u", 0))[1],
) for route in await ip_config.route_data
],
method=await self.get_ip_method(device,
f"ipv{ip_version}"),
gateway=str(await ip_config.gateway),
dns=[
str(ns["address"][1]) if ip_version == "4" else str(ns)
for ns in await getattr(
ip_config,
"nameserver_data" if ip_version ==
"4" else "nameservers",
)
],
)
configs.append(config)
return configs
async def get_wifi_info(
self, wifi_device: NetworkDeviceWireless
) -> Dict[str, Optional[Union[str, int]]]:
"""Получает информацию о Wi-Fi устройстве"""
active_ap = await wifi_device.active_access_point
wifi_info = {
"ssid": None,
"bssid": None,
"bitrate": await wifi_device.bitrate,
"strength": None,
"frequency": None,
"mode": None,
}
if active_ap != "/":
ap = AccessPoint(active_ap, self.system_bus)
wifi_info.update({
"ssid": await ap.ssid,
"bssid": await ap.hw_address,
"strength": await ap.strength,
"frequency": await ap.frequency,
"mode": await ap.mode,
})
return wifi_info
async def get_network_interfaces(
self,
initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[Union[NetworkInterface, WiFiDevice]]:
"""Получает список сетевых интерфейсов"""
devices_paths = await self.nm.get_devices()
network_interfaces = []
initial_positions = initial_positions or {}
for device_path in devices_paths:
generic_device = NetworkDeviceGeneric(device_path, self.system_bus)
interface_name = str(await generic_device.interface)
device_type = DeviceType(await generic_device.device_type).name
if device_type == "LOOPBACK":
continue
device_state = str(await generic_device.state)
if device_state == "10":
continue
common_data = {
"name":
interface_name,
"type":
device_type,
"device_state":
device_state,
"active_connection":
await self.get_connection_name(generic_device),
"mac_address":
str(await generic_device.hw_address),
"device_mtu":
str(await generic_device.mtu),
"ipv4":
IPConfig(configurations=await self.get_ip_config(
generic_device, "4")),
"ipv6":
IPConfig(configurations=await self.get_ip_config(
generic_device, "6")),
"x":
initial_positions.get(interface_name, (100, 100))[0],
"y":
initial_positions.get(interface_name, (100, 100))[1],
}
if device_type == "WIFI":
wifi_device = NetworkDeviceWireless(device_path,
self.system_bus)
wifi_info = await self.get_wifi_info(wifi_device)
network_interfaces.append(
WiFiDevice(**common_data, **wifi_info))
else:
network_interfaces.append(NetworkInterface(**common_data))
return network_interfaces
async def get_hostname(self) -> Dict[str, str]:
"""Возвращает имя хоста из команды hostname в формате JSON"""
try:
result = subprocess.run(["hostname"],
capture_output=True,
text=True,
check=True)
hostname = result.stdout.strip()
return {"hostname": hostname}
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Failed to execute hostname: {e}")
except Exception as e:
raise RuntimeError(f"Error processing hostname output: {e}")
async def get_interfaces_by_state(
self,
state: str,
initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[NetworkInterface]:
"""
Получает список интерфейсов по состоянию
"""
all_interfaces = await self.get_network_interfaces(initial_positions)
return [
interface for interface in all_interfaces
if interface.device_state == state
]
async def get_interfaces_container(
self,
state: Optional[str] = None,
initial_positions: Dict[str, Tuple[int, int]] = None,
container_x: int = 0,
container_y: int = 0,
) -> NetworkInterfacesContainer:
"""Получает объект, содержащий список интерфейсов и координаты контейнера"""
if state:
interfaces = await self.get_interfaces_by_state(
state, initial_positions)
else:
interfaces = await self.get_network_interfaces(initial_positions)
return NetworkInterfacesContainer(interfaces=interfaces,
container_x=container_x,
container_y=container_y)
async def get_network_interfaces_container(
self,
initial_positions: Optional[Dict[str, Tuple[int, int]]] = None,
) -> NetworkInterfacesContainer:
"""Получает контейнер с сетевыми интерфейсами и координатами контейнера"""
interfaces = await self.get_network_interfaces(initial_positions)
return NetworkInterfacesContainer(interfaces=interfaces,
container_x=0,
container_y=0)
async def get_activated_interfaces(
self,
initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[NetworkInterface]:
"""Получает список активных сетевых интерфейсов"""
return await self.get_interfaces_by_state("100", initial_positions)
async def get_deactivated_interfaces(
self,
initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[NetworkInterface]:
"""Получает список неактивных сетевых интерфейсов"""
all_interfaces = await self.get_network_interfaces(initial_positions)
return [
interface for interface in all_interfaces
if interface.device_state != "100"
]
async def get_activated_interfaces_container(
self,
initial_positions: Dict[str, Tuple[int, int]] = None,
container_x: int = 0,
container_y: int = 0,
) -> NetworkInterfacesContainer:
"""Получает объект, содержащий список активных интерфейсов и координаты контейнера"""
return await self.get_interfaces_container("100", initial_positions,
container_x, container_y)
async def get_deactivated_interfaces_container(
self,
initial_positions: Dict[str, Tuple[int, int]] = None,
container_x: int = 0,
container_y: int = 0,
) -> NetworkInterfacesContainer:
"""Получает объект, содержащий список неактивных интерфейсов и координаты контейнера"""
interfaces = await self.get_deactivated_interfaces(initial_positions)
return NetworkInterfacesContainer(interfaces=interfaces,
container_x=container_x,
container_y=container_y)