"""Catalog of ready-to-run cloud images for the quick VM flow. Each entry points to the vendor's official cloud image. Images boot in seconds and are configured through cloud-init (user, password, SSH key) — no installer. """ import re from typing import Any # osinfo ids must exist in osinfo-db shipped with Ubuntu 24.04+/Debian 13. CLOUD_IMAGES: list[dict[str, Any]] = [ { "id": "ubuntu-26.04", "title": "Ubuntu Server 26.04 LTS", "vendor": "Ubuntu", "hint": "Самая свежая LTS, поддержка до 2031 года. Рекомендуем.", "osinfo": "ubuntu24.04", "recommended": True, "login": "ubuntu", "arches": { "x86_64": {"url": "https://cloud-images.ubuntu.com/resolute/current/resolute-server-cloudimg-amd64.img", "sums": "https://cloud-images.ubuntu.com/resolute/current/SHA256SUMS", "algo": "sha256"}, "aarch64": {"url": "https://cloud-images.ubuntu.com/resolute/current/resolute-server-cloudimg-arm64.img", "sums": "https://cloud-images.ubuntu.com/resolute/current/SHA256SUMS", "algo": "sha256"}, }, }, { "id": "ubuntu-24.04", "title": "Ubuntu Server 24.04 LTS", "vendor": "Ubuntu", "hint": "Проверенная LTS, поддержка до 2029 года.", "osinfo": "ubuntu24.04", "login": "ubuntu", "arches": { "x86_64": {"url": "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img", "sums": "https://cloud-images.ubuntu.com/noble/current/SHA256SUMS", "algo": "sha256"}, "aarch64": {"url": "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-arm64.img", "sums": "https://cloud-images.ubuntu.com/noble/current/SHA256SUMS", "algo": "sha256"}, }, }, { "id": "debian-13", "title": "Debian 13", "vendor": "Debian", "hint": "Лёгкий и стабильный. Хорош для домашних сервисов.", "osinfo": "debian13", "login": "debian", "arches": { "x86_64": {"url": "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2", "sums": "https://cloud.debian.org/images/cloud/trixie/latest/SHA512SUMS", "algo": "sha512"}, "aarch64": {"url": "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-arm64.qcow2", "sums": "https://cloud.debian.org/images/cloud/trixie/latest/SHA512SUMS", "algo": "sha512"}, }, }, { "id": "alpine-3.23", "title": "Alpine Linux 3.23", "vendor": "Alpine", "hint": "Очень маленький: 200 МБ на диске, стартует мгновенно.", "osinfo": "alpinelinux3.21", "login": "alpine", "arches": { "x86_64": {"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/cloud/nocloud_alpine-3.23.0-x86_64-uefi-cloudinit-r0.qcow2", "sums": "https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/cloud/nocloud_alpine-3.23.0-x86_64-uefi-cloudinit-r0.qcow2.sha512", "algo": "sha512", "uefi": True}, "aarch64": {"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/cloud/nocloud_alpine-3.23.0-aarch64-uefi-cloudinit-r0.qcow2", "sums": "https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/cloud/nocloud_alpine-3.23.0-aarch64-uefi-cloudinit-r0.qcow2.sha512", "algo": "sha512", "uefi": True}, }, }, ] # OS families for the "install from ISO" flow. virt-install picks the right # virtual hardware from osinfo: Windows gets SATA/e1000e (works without extra # drivers), win11 additionally gets UEFI + TPM. OS_TYPES: list[dict[str, Any]] = [ {"id": "linux", "title": "Linux", "hint": "Ubuntu, Debian, Fedora и другие", "osinfo": "detect=on,require=off"}, {"id": "windows11", "title": "Windows 11 / Server 2025", "hint": "Нужны UEFI и TPM — включим сами", "osinfo": "win11"}, {"id": "windows10", "title": "Windows 10 / Server 2019–2022", "hint": "Классическая загрузка", "osinfo": "win10"}, {"id": "other", "title": "Другая система", "hint": "FreeBSD, старые ОС, экзотика", "osinfo": "generic"}, ] # Установщик Windows узнаём по метке тома ISO (у Microsoft вида CCCOMA_X64FRE_RU-RU_DV9) или по имени файла. WINDOWS_RE = re.compile(r"\bWIN(?:DOWS)?\s?(?:1[01]|7|8|SERVER)\b|\bWINDOWS\b|\b(?:X64|X86|A64|ARM64)FRE[EV]?\b") WINDOWS11_RE = re.compile(r"\bWIN(?:DOWS)?\s?11\b|\bSERVER\s?2025\b|\b(?:22000|22621|22631|26100|26200)\b") WINDOWS10_RE = re.compile(r"\bWIN(?:DOWS)?\s?(?:10|7|8)\b|\bLTSC\b|\bSERVER\s?20(?:16|19|22)\b|\b1\d{4}\b|\b20348\b") def guess_os_type(volume_label: str, filename: str) -> str: """Тип системы для установочного ISO: windows11 / windows10, если это установщик Windows, иначе "". Windows неизвестной версии — windows11: UEFI и TPM подходят и для Windows 10, а без них не встанет 11.""" text = re.sub(r"[^A-Z0-9]+", " ", f"{volume_label} {filename}".upper()) if not WINDOWS_RE.search(text): return "" if WINDOWS11_RE.search(text): return "windows11" return "windows10" if WINDOWS10_RE.search(text) else "windows11" def cloud_image(image_id: str) -> dict[str, Any] | None: return next((item for item in CLOUD_IMAGES if item["id"] == image_id), None) def os_type(type_id: str) -> dict[str, Any]: return next((item for item in OS_TYPES if item["id"] == type_id), OS_TYPES[0]) def image_filename(entry: dict[str, Any], arch: str) -> str: url = entry["arches"][arch]["url"] return url.rsplit("/", 1)[-1] def entry_for_filename(name: str) -> dict[str, Any] | None: """Catalog entry whose download has this file name (any architecture).""" for entry in CLOUD_IMAGES: for arch in entry["arches"].values(): if arch["url"].rsplit("/", 1)[-1] == name: return entry return None