253 lines
8.6 KiB
Python
253 lines
8.6 KiB
Python
"""
|
||
PCI Device Classifier
|
||
Classifies PCI devices based on lspci output and device class information.
|
||
"""
|
||
import re
|
||
from typing import Tuple, Optional, Dict, Any
|
||
|
||
|
||
class PCIClassifier:
|
||
"""
|
||
Classifier for PCI devices based on device class and characteristics.
|
||
"""
|
||
|
||
# PCI device class mappings to type_principal and sous_type
|
||
CLASS_MAPPINGS = {
|
||
# Storage devices
|
||
"SATA controller": ("PCI", "Contrôleur SATA"),
|
||
"NVMe": ("PCI", "SSD NVMe"),
|
||
"Non-Volatile memory controller": ("PCI", "SSD NVMe"),
|
||
"RAID bus controller": ("PCI", "Contrôleur RAID"),
|
||
"IDE interface": ("PCI", "Contrôleur IDE"),
|
||
"SCSI storage controller": ("PCI", "Contrôleur SCSI"),
|
||
|
||
# Network devices
|
||
"Ethernet controller": ("PCI", "Carte réseau Ethernet"),
|
||
"Network controller": ("PCI", "Carte réseau"),
|
||
"Wireless controller": ("PCI", "Carte WiFi"),
|
||
|
||
# Graphics
|
||
"VGA compatible controller": ("PCI", "Carte graphique"),
|
||
"3D controller": ("PCI", "Carte graphique"),
|
||
"Display controller": ("PCI", "Carte graphique"),
|
||
|
||
# Audio
|
||
"Audio device": ("PCI", "Carte son"),
|
||
"Multimedia audio controller": ("PCI", "Carte son"),
|
||
|
||
# USB
|
||
"USB controller": ("PCI", "Contrôleur USB"),
|
||
|
||
# System infrastructure
|
||
"Host bridge": ("PCI", "Pont système"),
|
||
"PCI bridge": ("PCI", "Pont PCI"),
|
||
"ISA bridge": ("PCI", "Pont ISA"),
|
||
"SMBus": ("PCI", "Contrôleur SMBus"),
|
||
"IOMMU": ("PCI", "Contrôleur IOMMU"),
|
||
|
||
# Security
|
||
"Encryption controller": ("PCI", "Contrôleur de chiffrement"),
|
||
|
||
# Other
|
||
"Serial controller": ("PCI", "Contrôleur série"),
|
||
"Communication controller": ("PCI", "Contrôleur de communication"),
|
||
"Signal processing controller": ("PCI", "Contrôleur de traitement du signal"),
|
||
}
|
||
|
||
@staticmethod
|
||
def classify_device(
|
||
device_section: str,
|
||
device_info: Optional[Dict[str, Any]] = None
|
||
) -> Tuple[str, str]:
|
||
"""
|
||
Classify a PCI device based on lspci output.
|
||
|
||
Args:
|
||
device_section: Full lspci -v output for a single device
|
||
device_info: Optional pre-parsed device information
|
||
|
||
Returns:
|
||
Tuple of (type_principal, sous_type)
|
||
"""
|
||
if not device_info:
|
||
from app.utils.lspci_parser import parse_device_info
|
||
device_info = parse_device_info(device_section)
|
||
|
||
device_class = device_info.get("device_class", "")
|
||
description = device_info.get("device_name", "")
|
||
vendor_name = device_info.get("vendor_name", "")
|
||
|
||
# Strategy 1: Direct class mapping
|
||
for class_key, (type_principal, sous_type) in PCIClassifier.CLASS_MAPPINGS.items():
|
||
if class_key.lower() in device_class.lower():
|
||
# Refine network devices
|
||
if sous_type == "Carte réseau":
|
||
refined = PCIClassifier.refine_network_type(device_section, description)
|
||
if refined:
|
||
return ("PCI", refined)
|
||
return (type_principal, sous_type)
|
||
|
||
# Strategy 2: Keyword detection in description
|
||
keyword_result = PCIClassifier.detect_from_keywords(device_section, description)
|
||
if keyword_result:
|
||
return ("PCI", keyword_result)
|
||
|
||
# Strategy 3: Vendor-specific detection
|
||
vendor_result = PCIClassifier.detect_from_vendor(vendor_name, description)
|
||
if vendor_result:
|
||
return ("PCI", vendor_result)
|
||
|
||
# Default: Generic PCI device
|
||
return ("PCI", "Autre")
|
||
|
||
@staticmethod
|
||
def refine_network_type(content: str, description: str) -> Optional[str]:
|
||
"""
|
||
Refine network device classification (WiFi vs Ethernet).
|
||
|
||
Args:
|
||
content: Full device section
|
||
description: Device description
|
||
|
||
Returns:
|
||
Refined sous_type or None
|
||
"""
|
||
normalized = content.lower() + " " + description.lower()
|
||
|
||
# WiFi patterns
|
||
wifi_patterns = [
|
||
r"wi[‑-]?fi", r"wireless", r"802\.11[a-z]", r"wlan",
|
||
r"wireless\s+adapter", r"wireless\s+network",
|
||
r"atheros", r"qualcomm.*wireless", r"broadcom.*wireless",
|
||
r"intel.*wireless", r"realtek.*wireless"
|
||
]
|
||
|
||
for pattern in wifi_patterns:
|
||
if re.search(pattern, normalized, re.IGNORECASE):
|
||
return "Carte WiFi"
|
||
|
||
# Ethernet patterns
|
||
ethernet_patterns = [
|
||
r"ethernet", r"gigabit", r"10/100", r"1000base",
|
||
r"rtl81\d+", r"e1000", r"bnx2", r"tg3"
|
||
]
|
||
|
||
for pattern in ethernet_patterns:
|
||
if re.search(pattern, normalized, re.IGNORECASE):
|
||
return "Carte réseau Ethernet"
|
||
|
||
return None
|
||
|
||
@staticmethod
|
||
def detect_from_keywords(content: str, description: str) -> Optional[str]:
|
||
"""
|
||
Detect device type from keywords in content and description.
|
||
|
||
Args:
|
||
content: Full device section
|
||
description: Device description
|
||
|
||
Returns:
|
||
Detected sous_type or None
|
||
"""
|
||
normalized = content.lower() + " " + description.lower()
|
||
|
||
keyword_mappings = [
|
||
# Storage
|
||
(r"nvme|ssd.*pcie|non-volatile.*memory", "SSD NVMe"),
|
||
(r"sata|ahci", "Contrôleur SATA"),
|
||
|
||
# Network
|
||
(r"wi[‑-]?fi|wireless|802\.11", "Carte WiFi"),
|
||
(r"ethernet|gigabit|network", "Carte réseau Ethernet"),
|
||
|
||
# Graphics
|
||
(r"nvidia|geforce|quadro|rtx|gtx", "Carte graphique"),
|
||
(r"amd.*radeon|rx\s*\d+", "Carte graphique"),
|
||
(r"intel.*graphics|intel.*hd", "Carte graphique"),
|
||
(r"vga|display|graphics", "Carte graphique"),
|
||
|
||
# Audio
|
||
(r"audio|sound|hda|ac97", "Carte son"),
|
||
|
||
# USB
|
||
(r"xhci|ehci|ohci|uhci|usb.*host", "Contrôleur USB"),
|
||
]
|
||
|
||
for pattern, sous_type in keyword_mappings:
|
||
if re.search(pattern, normalized, re.IGNORECASE):
|
||
return sous_type
|
||
|
||
return None
|
||
|
||
@staticmethod
|
||
def detect_from_vendor(vendor_name: str, description: str) -> Optional[str]:
|
||
"""
|
||
Detect device type from vendor name and description.
|
||
|
||
Args:
|
||
vendor_name: Vendor name
|
||
description: Device description
|
||
|
||
Returns:
|
||
Detected sous_type or None
|
||
"""
|
||
if not vendor_name:
|
||
return None
|
||
|
||
vendor_lower = vendor_name.lower()
|
||
|
||
# GPU vendors
|
||
if any(v in vendor_lower for v in ["nvidia", "amd", "intel", "ati"]):
|
||
if any(k in description.lower() for k in ["geforce", "radeon", "quadro", "graphics", "vga"]):
|
||
return "Carte graphique"
|
||
|
||
# Network vendors
|
||
if any(v in vendor_lower for v in ["realtek", "intel", "broadcom", "qualcomm", "atheros"]):
|
||
if any(k in description.lower() for k in ["ethernet", "network", "wireless", "wifi", "802.11"]):
|
||
if any(k in description.lower() for k in ["wireless", "wifi", "802.11"]):
|
||
return "Carte WiFi"
|
||
return "Carte réseau Ethernet"
|
||
|
||
# Storage vendors
|
||
if any(v in vendor_lower for v in ["samsung", "crucial", "micron", "western digital", "seagate"]):
|
||
if "nvme" in description.lower():
|
||
return "SSD NVMe"
|
||
|
||
return None
|
||
|
||
@staticmethod
|
||
def extract_technical_specs(device_info: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
Extract technical specifications for caracteristiques_specifiques field.
|
||
|
||
Args:
|
||
device_info: Parsed device information
|
||
|
||
Returns:
|
||
Dictionary with technical specifications
|
||
"""
|
||
specs = {
|
||
"slot": device_info.get("slot"),
|
||
"device_class": device_info.get("device_class"),
|
||
"vendor_name": device_info.get("vendor_name"),
|
||
"subsystem": device_info.get("subsystem"),
|
||
"driver": device_info.get("driver"),
|
||
"iommu_group": device_info.get("iommu_group"),
|
||
}
|
||
|
||
# Add vendor:device ID if available
|
||
if device_info.get("vendor_device_id"):
|
||
specs["pci_device_id"] = device_info.get("vendor_device_id")
|
||
|
||
# Add revision if available
|
||
if device_info.get("revision"):
|
||
specs["revision"] = device_info.get("revision")
|
||
|
||
# Add modules if available
|
||
if device_info.get("modules"):
|
||
specs["modules"] = ", ".join(device_info.get("modules", []))
|
||
|
||
# Clean None values
|
||
return {k: v for k, v in specs.items() if v is not None}
|