feat(images): backend catalog + mirror model + download API/worker
Phase 1 of VM image mirror feature: - StorageMixin.download_url (PVE download-url/aria2) + get_download_target_storages - builtin curated cloud-image catalog (Ubuntu/Debian/Rocky/Alma, amd64+arm64) - ImageMirror model for admin custom mirrors - /proxmox/api/images: catalog, lxc-templates proxy, storages, download - _do_image_download_sync worker with log-tailing progress (DeployTask) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ from .tasks import TaskQueue, ProxmoxTask, DeployTask
|
||||
from .metrics import InstanceMetric, InstanceNicMetric
|
||||
from .settings import PanelSettings, SecuritySetting, Workspace, WorkspaceServer, WorkspaceUser
|
||||
from .misc import Notification, NotificationPreference, BackupJob, AuditLog
|
||||
from .catalog import ImageMirror
|
||||
|
||||
__all__ = [
|
||||
'Base', 'utcnow',
|
||||
@@ -15,5 +16,6 @@ __all__ = [
|
||||
'TaskQueue', 'ProxmoxTask', 'DeployTask',
|
||||
'InstanceMetric', 'InstanceNicMetric',
|
||||
'PanelSettings', 'SecuritySetting', 'Workspace', 'WorkspaceServer', 'WorkspaceUser',
|
||||
'Notification', 'NotificationPreference', 'BackupJob', 'AuditLog'
|
||||
'Notification', 'NotificationPreference', 'BackupJob', 'AuditLog',
|
||||
'ImageMirror'
|
||||
]
|
||||
|
||||
54
backend/app/models/catalog.py
Normal file
54
backend/app/models/catalog.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from .base import *
|
||||
|
||||
|
||||
class ImageMirror(Base):
|
||||
"""
|
||||
Кастомный источник образов (зеркало), добавленный администратором.
|
||||
Дополняет встроенный curated-каталог (app/catalog/builtin.py).
|
||||
|
||||
kind:
|
||||
'qcow2' — cloud-образ ВМ (url обязателен)
|
||||
'vztmpl' — LXC-шаблон (url ИЛИ template — имя файла из репозитория)
|
||||
"""
|
||||
__tablename__ = "image_mirrors"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String(150), nullable=False)
|
||||
kind = Column(String(20), nullable=False, default='qcow2', index=True)
|
||||
os = Column(String(50), nullable=True)
|
||||
version = Column(String(50), nullable=True)
|
||||
arch = Column(String(20), nullable=False, default='amd64')
|
||||
|
||||
url = Column(String(1000), nullable=True)
|
||||
template = Column(String(255), nullable=True)
|
||||
|
||||
checksum = Column(String(150), nullable=True)
|
||||
checksum_algorithm = Column(String(20), nullable=True)
|
||||
|
||||
description = Column(Text, nullable=True)
|
||||
enabled = Column(Boolean, default=True, nullable=False)
|
||||
|
||||
created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'id': f'mirror-{self.id}',
|
||||
'mirror_id': self.id,
|
||||
'source': 'mirror',
|
||||
'kind': self.kind,
|
||||
'os': self.os,
|
||||
'version': self.version,
|
||||
'arch': self.arch,
|
||||
'name': self.name,
|
||||
'url': self.url,
|
||||
'template': self.template,
|
||||
'checksum': self.checksum,
|
||||
'checksum_algorithm': self.checksum_algorithm,
|
||||
'description': self.description,
|
||||
'enabled': self.enabled,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ImageMirror(id={self.id}, name='{self.name}', kind='{self.kind}')>"
|
||||
@@ -143,6 +143,8 @@ class DeployTask(Base):
|
||||
'reinstall': f"Переустановка {self.name}",
|
||||
'clone': f"Клонирование {self.name}",
|
||||
'change_password': f"Смена пароля: {self.name}",
|
||||
'image_download': f"Загрузка образа: {self.name}",
|
||||
'image_template': f"Шаблон из образа: {self.name}",
|
||||
}
|
||||
return {
|
||||
'id': self.id,
|
||||
|
||||
Reference in New Issue
Block a user