Files
JoinDomainGUI/domainjoin/backend/linux_realmd.py
T
tuxgyverandClaude Sonnet 5 1feb72bf17 Version initiale : diagnostic réseau AD + jointure de domaine
Application PySide6 (GUI + CLI) fonctionnant sous Linux et Windows :
- Diagnostic réseau complet AD (DNS, SRV, ports, NTP, outils requis)
- Jointure de domaine via realmd (Linux) ou Add-Computer (Windows)
- Installation automatique des prérequis Linux (apt)
- CLI colorée (diagnose/join/install-prereqs), sans dépendance PySide6
- Scripts de packaging PyInstaller pour Linux et Windows

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 11:40:11 +02:00

135 lines
4.6 KiB
Python

"""Backend de jointure de domaine pour Linux, via realmd (`realm join`)."""
from __future__ import annotations
import os
import shutil
import subprocess
from typing import Optional
from .base import DomainJoinBackend, JoinResult, OutputCallback
REQUIRED_PACKAGES = ["realmd", "sssd", "adcli", "samba-common-bin", "packagekit"]
INSTALL_HINT = "sudo apt install " + " ".join(REQUIRED_PACKAGES)
class LinuxRealmdBackend(DomainJoinBackend):
def is_available(self) -> tuple[bool, str]:
if shutil.which("realm") is None:
return False, f"La commande 'realm' est introuvable. Installez-la avec : {INSTALL_HINT}"
return True, "realmd disponible."
def can_install_prerequisites(self) -> bool:
return shutil.which("apt-get") is not None
def install_prerequisites(self, on_output: Optional[OutputCallback] = None) -> JoinResult:
def emit(line: str) -> None:
if on_output:
on_output(line)
if shutil.which("apt-get") is None:
return JoinResult(success=False, message="apt-get est introuvable sur ce système.")
env = os.environ.copy()
env["DEBIAN_FRONTEND"] = "noninteractive"
for cmd in (["apt-get", "update"], ["apt-get", "install", "-y", *REQUIRED_PACKAGES]):
emit(f"$ {' '.join(cmd)}")
try:
process = subprocess.Popen(
cmd,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env,
)
except OSError as exc:
return JoinResult(success=False, message=f"Impossible de lancer apt-get : {exc}")
if process.stdout:
for line in process.stdout:
emit(line.rstrip("\n"))
returncode = process.wait()
if returncode != 0:
return JoinResult(
success=False,
message=f"Échec de « {' '.join(cmd)} » (code retour {returncode}). "
"Vérifiez que l'application tourne avec les droits root.",
)
available, _ = self.is_available()
if available:
return JoinResult(success=True, message="Les prérequis ont été installés avec succès.")
return JoinResult(
success=False,
message="Installation terminée mais 'realm' reste introuvable. Vérifiez le journal ci-dessus.",
)
def join(
self,
domain: str,
username: str,
password: str,
ou: Optional[str] = None,
computer_name: Optional[str] = None,
on_output: Optional[OutputCallback] = None,
) -> JoinResult:
available, reason = self.is_available()
if not available:
return JoinResult(success=False, message=reason)
def emit(line: str) -> None:
if on_output:
on_output(line)
if computer_name:
emit(
"Note : realm join utilise le nom d'hôte système du poste comme nom de "
"machine dans l'annuaire. Changez le hostname (hostnamectl) avant la "
f"jointure si vous voulez que la machine apparaisse comme « {computer_name} »."
)
cmd = ["realm", "-v", "join", "--user", username]
if ou:
cmd += ["--computer-ou", ou]
cmd.append(domain)
emit(f"$ realm -v join --user {username}" + (f" --computer-ou {ou}" if ou else "") + f" {domain}")
try:
process = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
)
except OSError as exc:
return JoinResult(success=False, message=f"Impossible de lancer realm : {exc}")
try:
process.stdin.write(password + "\n")
process.stdin.close()
except BrokenPipeError:
pass
if process.stdout:
for line in process.stdout:
emit(line.rstrip("\n"))
returncode = process.wait()
if returncode == 0:
return JoinResult(
success=True,
message=f"Le poste a rejoint le domaine {domain} avec succès.",
needs_reboot=True,
)
return JoinResult(
success=False,
message=f"Échec de la jointure au domaine (code retour {returncode}). Voir le journal ci-dessus.",
)