Backend: new proxmox mixins and API routes for firewall, pools, access
control, node admin and disks; extend storage and VM endpoints.
Frontend: node managers (firewall, pool, access, disk, node admin), VM
firewall and hardware tabs, PCI/USB device dialogs, related hooks and
locale strings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Migrate VMs/LXCs between cluster nodes with live progress tracking
- Configure autostart, boot order, startup/shutdown delays, and delete protection from the instance settings tab
- Group OS templates by detected OS (Ubuntu, AlmaLinux, etc.) instead of a single "Cloud Images" bucket
- Expand native Proxmox backup job management (schedule, compression, retention, guest selection, notifications)
- Add ru/en translations for the above
- update_vm_config: whitelist netN/scsiN/virtioN/ideN/sataN/ipconfigN by
regex instead of hardcoded 0-3; accept validated delete='netN' key;
raise with real Proxmox error text instead of returning False
- update_container_config: refactor to dict signature with whitelist +
netN + delete (fixes LXC config PUT endpoint that passed the whole
dict as hostname)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- wait for clone lock release before config.put (cross-node clones kept
lock past wait_for_task, dropping ALL requested settings silently)
- configure_vm applies params in independent groups (compute/network/
cloudinit/disk) with per-group errors and lock retry
- set sockets=1 with cores so total vCPU matches request
- keep template net0 model/MAC/options, replace only bridge
- disk resize: pick boot disk via regex over all buses, report failures
- surface config errors as deploy warnings in UI toast instead of
silent success
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add per-user quotas (max instances, vCPU, RAM, disk) enforced at deploy
time and surfaced to users self-service.
Backend:
- UserQuota model (one-to-one, NULL = unlimited) + migration #28
- quota_service: usage from vm_instances cache, check_quota raises 429
- enforce in VM (templates.py) and LXC (lxc.py) deploy paths
- RBAC quota:view / quota:manage, granted to admin
- admin API GET/PUT /admin/api/users/{id}/quota
- self-service GET /settings/api/quota
- tighten VM list isolation via can_view_all_instances
Frontend:
- admin quota dialog in UsersPage with usage bars
- quota card on Settings page (own limits)
- remaining-quota hint in CreateInstanceWizard config step
- useUserQuota/useSetUserQuota, useMyQuota hooks; RU/EN locales
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an email-based "forgot password" flow:
- PasswordResetToken model (stores only a SHA-256 hash, single-use, TTL)
and migration 27 for the password_reset_tokens table.
- POST /api/auth/forgot-password — generic response (no account
enumeration), 60s throttle, emails a one-time reset link via the
existing EmailChannel as a background task.
- GET /api/auth/reset-password/validate — token validity check for the UI.
- POST /api/auth/reset-password — validates the token, enforces the
password policy, sets the new password, clears lock state, invalidates
outstanding tokens and terminates all sessions.
- Configurable link lifetime via PASSWORD_RESET_TOKEN_EXPIRE_MINUTES (30m).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Retrying a template build failed at download because the qcow2 from a prior
attempt already existed and download-url refuses to overwrite. Now skip the
download when the volume already exists (volume_exists), and surface the PVE
task-log tail on download failure instead of a generic message.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
create_vm_with_import got the display name ('Ubuntu 22.04 LTS (Jammy) Cloud')
which PVE rejects (Parameter verification failed on 'name'). Sanitize to a
DNS-safe name (e.g. ubuntu-22-04-lts-jammy-cloud). Also stop attributing every
create failure to 'import unsupported' — only say that when the error mentions
import; otherwise surface the real error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Proxmox content=import rejects Ubuntu cloud '*.img' with 'invalid filename
or wrong extension' though the format is qcow2. Rename non-import-extension
files to .qcow2 before download-url; import-from auto-detects the format.
Applied to both download and template workers (download filename + import volid).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET /proxmox/api/{server_id}/images/node-arch derives amd64/arm64 from
node status current-kernel.machine; returns null when node unreachable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET/POST/PUT/DELETE /proxmox/api/images/mirrors (perm template:manage).
Enabled mirrors merge into the catalog feed automatically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Proxmox's guest-agent exec (PVE/QemuServer/Agent.pm) dies with
"Wide character in subroutine entry" when input-data contains non-ASCII
bytes. The growpart script had Cyrillic comments, so the whole auto-grow
call returned 500 and the FS was never resized. Replace those comments
with ASCII so the script reaches the guest.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend fixes for VM/LXC disk resize:
- resize_vm_disk/resize_container_disk now honor wait_for_task and raise
the real Proxmox exitstatus (e.g. "disk 'scsi0' does not exist") instead
of always returning success.
- add grow_vm_filesystem: after a VM resize, rescan the disk (retry loop,
needed for SATA/SCSI), growpart the partition and resize the FS
(ext*/xfs/btrfs) via the guest agent; surface its output to the API.
- execute_script normalizes CRLF/CR to LF so pasted scripts and growpart
don't break on trailing \r ("partition-number must be a number").
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The monitoring worker wrote Proxmox maxmem/maxdisk (bytes) straight into
the memory/disk_size columns, which the rest of the app stores as MB/GB.
Every sync cycle clobbered correct values with bytes, so the saved-config
card showed huge byte numbers under MB/GB labels. Convert via new
_bytes_to_mb/_bytes_to_gb helpers; existing rows self-heal on next sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _do_clone_sync now tails the PVE clone task log and maps the disk
transfer percentage to deploy progress 50→95% instead of a static 70%.
- Add owner_id to CloneRequest; admins may assign the clone to another
user (mirrors LXC deploy). Register the cloned instance via
save_vm_instance so it has an owner and is visible to limited users.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
create_vm_snapshot/create_container_snapshot returned success as soon as
the POST returned a UPID, so a snapshot that failed asynchronously (e.g.
storage without snapshot support) still reported "created". Now wait on
the task and surface the actual exitstatus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_parse_version split on "." only, so "2-1" hit int() and the whole
version collapsed to (0,) — update checks ignored build bumps and could
even offer a "downgrade". Extract every numeric run via regex instead:
"1.5.2-1" -> (1, 5, 2, 1).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Store UI language per user (users.language column, migration 26).
Language selector moves from Panel tab to Profile tab — each account
picks its own language, saved to DB and applied via i18n.changeLanguage()
on save so the interface switches immediately.
Panel tab: panel_name read-only (falls back to env PANEL_NAME),
log_retention_days field exposed in UI, language selector removed.
Panel API accepts panel_name in PUT so it can be overridden in DB
without touching the env var.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename SMTP request/response fields (host/port/username/password/from_email/tls)
and point security settings hooks at /admin/api/security/settings with a typed
SecuritySettings shape. Add minutes / security_saved i18n keys.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add live per-disk usage, disk read/write and network in/out rates to the
instance Overview tab, the instances list and the dashboard top-loaded list.
Backend:
- get_vm_fsinfo() pulls per-filesystem usage via QEMU guest agent
- metrics_broadcaster computes I/O rates (bytes/sec) from cumulative counters
- instances-list loop now refreshes each running VM via status/current
(1s-granular) instead of cluster/resources (10s pvestatd), with bounded
concurrency, and paces both realtime loops to a 1s cadence
Frontend:
- OverviewTab: per-disk cards, Network I/O and I/O-rate cards via WS
- InstancesPage: Net and Disk I/O columns; Disk% shown for LXC only
- DashboardPage: top-loaded list overlaid with realtime metrics
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Validate resource name in auto-allocate form before sending request
- Fill resource_name, proxmox_node, resource_type, mac_address when syncing
a VM that already has matching vmid/server_id but missing metadata
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The mock_proxmox fixture patched ProxmoxClient with return_value= (constructor
style), but since commit d66928e clients are built via the
ProxmoxClient.from_server classmethod. from_server() therefore returned an
unconfigured child mock, so endpoints serialized a MagicMock and responded 500
— failing 5 tests in test_api_vms.py on every CI run (Backend Tests workflow).
Wire .from_server.return_value (and .return_value) on all patched ProxmoxClient
references to the configured instance so every construction path resolves to
the same mock. Full suite now green (60 passed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Roles are stored in the new resource:action format and any legacy keys are
converted once at startup by the RBAC v2 DB migration (migrate_rbac_v2), so
the permission engine no longer needs to resolve legacy codes on every check.
- get_user_permissions: store role permission codes as-is (no dual add).
- has_permission: match new-format codes only; drop resolve_permission and the
dotted-code fallbacks.
- Remove now-unused resolve_permission import in middleware.
- LEGACY_PERMISSION_MAP / convert_* are retained as one-shot DB-migration
utilities (upgrade path), not a runtime bridge.
- Tests: adapt fixtures to new-format role permissions; assert legacy keys are
no longer honored at request time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace all legacy dotted permission codes (vms.view, proxmox.manage, ...)
in PermissionChecker/has_permission across app/api with the new
resource:action format (vm:view, server:manage, ...), per LEGACY_PERMISSION_MAP.
All 33 codes used by the API are now registered in the permission registry.
Also switch the /api/auth/me admin branch to get_all_permissions_v2 so the
endpoint emits permissions in the new format only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
These permissions were referenced by the API (users.manage, proxmox.vm.manage)
but never registered in the permission registry, so they only worked through
the is_admin bypass — non-admin users granted them silently received 403.
- Register user:manage and vm:manage in the registry.
- Add missing legacy mappings: users.manage->user:manage,
settings.manage->setting:manage.
- Grant both new permissions to the admin role (defaults + init.sql seed);
ensure_default_roles_new_format propagates them to existing deployments.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collapse four duplicated host+auth client-building blocks into a single
ProxmoxClient.from_server() classmethod (the canonical source of truth):
- _helpers._get_proxmox_client now delegates to from_server (107 callers
untouched)
- ipam._build_proxmox_client removed; its one call site uses from_server
- task_queue_service keeps its per-server cache pool, builds via from_server
- monitoring_worker keeps its ValueError-on-missing-auth contract, builds
via from_server
Behaviour preserved (token-first, then password; non-default port appended).
Verified: containers rebuild healthy, smoke test of both auth paths passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the /api/servers/auto-setup endpoint, the AutoSetupDialog UI and the
useAutoSetupServer hook. Rename the auto_setup locale strings to
auto_create_token. Also normalize the storages response to an array.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the guest-agent exec-status polling loop into a shared _wait_exec_status
method reused by execute_command and execute_script. No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop dead route handlers that have an active replacement and zero callers
(frontend, backend, tests):
- create-lxc, create-lxc-smart -> superseded by POST /lxc/deploy
- clone-lxc -> superseded by container/{vmid}/clone
- {server_id}/lxc-templates -> duplicate of /lxc-templates/{server_id}
- dashboard/stats -> dashboard uses resources/all + alerts
- permissions (v1) -> superseded by permissions/v2
The identically-named ProxmoxClient/SecurityService methods are kept (they
back the surviving endpoints).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add target node and storage overrides to single and bulk restore flows, and
track Proxmox task progress (parse_task_progress in the monitoring worker,
new ProxmoxTask progress column + migration, enriched task API responses).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When Proxmox API returns 400 "unable to parse worker upid", the monitoring
worker was getting empty status {}, treating it as running, and retrying
forever every 5 seconds, causing 100% CPU and log spam.
Now when get_task_status() returns empty dict, we immediately mark the task
as failed with exit_status="Invalid UPID" and move on instead of looping.
Fixes production deployment hang.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- get_vm_status and get_container_status were missing require_vm_access(),
allowing any user with vms.view to see any VM status regardless of ownership.
Now users with vms.view.own can only access their own VMs (403 otherwise).
- Downgrade upload-artifact@v4 → @v3: v4 is not supported on Gitea (GHES)
and caused CI failures on every run.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The ssh_keys module was imported in main.py but missing from api/__init__.py,
potentially causing import issues. Added to ensure all API routers are
explicitly declared and available.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The migrations/ directory was missing from the Dockerfile COPY instructions,
causing ModuleNotFoundError when app.main tries to import and run migrations
during startup. This caused production deployments to fail with:
ModuleNotFoundError: No module named 'migrations'
The automatic migration runner in main.py (line 157) needs this directory to:
1. Detect database schema changes
2. Run pending migrations
3. Initialize default admin user
Fixes production deployment failure when containers start.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>