Исправлены ошибки второго раунда аудита: локаль virsh, имена машин, IP, ISO в приводе, консоль

- все команды запускаются с LC_ALL=C.UTF-8: русский перевод virsh ломал разбор состояния машин
- имя машины не может быть числом или UUID (virsh принял бы его за ID другой машины)
- IP без MAC машины не угадывается по чужой аренде DHCP; у выключенных машин нет устаревшего IP
- вставленный ISO определяется по устройству CD-ROM, а не по «.iso» в пути
- ISO, вставленный в привод машины, нельзя удалить — машина перестала бы запускаться
- на ARM-платах нет ложного предупреждения про флаги vmx/svm
- проверка машины в консоли не блокирует остальные запросы

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Этот коммит содержится в:
Claude
2026-09-25 15:17:23 +00:00
родитель e5ce9c4d41
Коммит 7ec3dc6082
7 изменённых файлов: 105 добавлений и 22 удалений
+15 -5
Просмотреть файл
@@ -26,6 +26,13 @@ class NetworkError(Exception):
pass
def valid_vm_name(name: str) -> bool:
"""Same rule as core.valid_vm_name (kept here: this module does not import core)."""
if not re.fullmatch(r'[a-zA-Z0-9][a-zA-Z0-9_.-]{1,62}', name or ''):
return False
return not name.isdigit() and not re.fullmatch(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}', name)
def run_cmd(cmd: list[str], timeout: int = 12) -> dict[str, Any]:
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, check=False)
@@ -206,7 +213,7 @@ def _net_update(action: str, host_xml: str) -> dict[str, Any]:
def reserve_nat_address(vm_name: str, mac: str) -> str | None:
"""Pin the machine to one NAT address for its whole life; None when libvirt refused."""
if not re.fullmatch(r'[a-zA-Z0-9][a-zA-Z0-9_.-]{1,62}', vm_name or ''):
if not valid_vm_name(vm_name):
return None
reservations = nat_reservations()
for item in reservations:
@@ -360,13 +367,16 @@ def resolve_vm_ip(vm_name: str) -> str | None:
if match:
return match.group(1)
macs = set(vm_mac_addresses(vm_name))
if not macs:
# Without the machine's MAC any lease would be a guess — possibly another machine's address.
return None
leases = run_cmd(['virsh', 'net-dhcp-leases', NETWORK_NAME], timeout=8)
if not leases['ok']:
return None
macs = set(vm_mac_addresses(vm_name))
for line in leases['stdout'].splitlines():
low = line.lower()
if macs and not any(mac in low for mac in macs):
if not any(mac in low for mac in macs):
continue
match = re.search(r'\b(192\.168\.100\.\d+|\d+\.\d+\.\d+\.\d+)/\d+', line)
if match:
@@ -383,7 +393,7 @@ def tcp_connect_check(host: str, port: int, timeout: float = 2.0) -> dict[str, A
def add_port_forward(vm_name: str, guest_ip: str, external_port: Any, guest_port: Any, protocol: str, note: str = '') -> dict[str, Any]:
if not vm_name or not re.fullmatch(r'[a-zA-Z0-9][a-zA-Z0-9_.-]{1,62}', vm_name):
if not valid_vm_name(vm_name):
raise NetworkError('Некорректное имя VM')
if guest_ip == 'auto':
resolved_ip = resolve_vm_ip(vm_name)
@@ -611,7 +621,7 @@ def find_matching_forward(forwards: list[dict[str, Any]], vm_name: str, external
def diagnose_public_access(vm_name: str, external_port: int, guest_port: int, protocol: str = 'tcp') -> dict[str, Any]:
if not vm_name or not re.fullmatch(r'[a-zA-Z0-9][a-zA-Z0-9_.-]{1,62}', vm_name):
if not valid_vm_name(vm_name):
raise NetworkError('Некорректное имя VM')
if not valid_port(external_port) or not valid_port(guest_port):
raise NetworkError('Порт должен быть от 1 до 65535')