Stop asking to confirm host directories before an OCI update

This commit is contained in:
MacRimi
2026-09-26 02:21:35 +02:00
parent 1e6b8b1c20
commit e14a9911fd
15 changed files with 20 additions and 62 deletions
+1 -20
View File
@@ -951,7 +951,6 @@ export function VirtualMachines() {
// Updates tab replaces the image instead of updating packages. // Updates tab replaces the image instead of updating packages.
const [ociInstance, setOciInstance] = useState<OciInstanceInfo | null>(null) const [ociInstance, setOciInstance] = useState<OciInstanceInfo | null>(null)
const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "recreate" } | null>(null) const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "recreate" } | null>(null)
const [scheduleAckExternal, setScheduleAckExternal] = useState(false)
// Firewall log state — fetched only when the operator opens that tab // Firewall log state — fetched only when the operator opens that tab
// so a CT/VM without firewall use doesn't pay the pvesh cost on every // so a CT/VM without firewall use doesn't pay the pvesh cost on every
@@ -2035,7 +2034,6 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
? s.targets.map((value: any) => String(value)) ? s.targets.map((value: any) => String(value))
: ([...(legacyTarget !== "app" ? ["os"] : []), ...(legacyTarget !== "os" ? ["apps"] : [])])) : ([...(legacyTarget !== "app" ? ["os"] : []), ...(legacyTarget !== "os" ? ["apps"] : [])]))
setScheduleReleaseDelayDays(Number.isInteger(Number(s.release_delay_days)) ? Number(s.release_delay_days) : 0) setScheduleReleaseDelayDays(Number.isInteger(Number(s.release_delay_days)) ? Number(s.release_delay_days) : 0)
setScheduleAckExternal(s.acknowledge_external_data === true)
if (s.backup !== undefined) setApplyBackup(!!s.backup) if (s.backup !== undefined) setApplyBackup(!!s.backup)
if (s.backup_storage) setApplyBackupStorage(s.backup_storage) if (s.backup_storage) setApplyBackupStorage(s.backup_storage)
if (s.restart !== undefined) setApplyRestart(!!s.restart) if (s.restart !== undefined) setApplyRestart(!!s.restart)
@@ -2206,7 +2204,6 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
backup: applyBackup, backup: applyBackup,
backup_storage: applyBackupStorage || selectedBackupStorage || "", backup_storage: applyBackupStorage || selectedBackupStorage || "",
restart: ociInstance ? false : applyRestart, restart: ociInstance ? false : applyRestart,
acknowledge_external_data: ociInstance ? scheduleAckExternal : false,
}), }),
}) })
if (cronToSave.trim()) setScheduleConfigured(true) if (cronToSave.trim()) setScheduleConfigured(true)
@@ -2228,7 +2225,6 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
scheduleTarget: scheduleTarget, scheduleTarget: scheduleTarget,
scheduleTargets: [...scheduleTargets], scheduleTargets: [...scheduleTargets],
scheduleReleaseDelayDays: scheduleReleaseDelayDays, scheduleReleaseDelayDays: scheduleReleaseDelayDays,
scheduleAckExternal: scheduleAckExternal,
}) })
setOptionsEditMode(true) setOptionsEditMode(true)
} }
@@ -2243,7 +2239,6 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
setScheduleTarget(optionsSnapshot.scheduleTarget) setScheduleTarget(optionsSnapshot.scheduleTarget)
setScheduleTargets(optionsSnapshot.scheduleTargets || ["os", "apps"]) setScheduleTargets(optionsSnapshot.scheduleTargets || ["os", "apps"])
setScheduleReleaseDelayDays(optionsSnapshot.scheduleReleaseDelayDays) setScheduleReleaseDelayDays(optionsSnapshot.scheduleReleaseDelayDays)
setScheduleAckExternal(!!optionsSnapshot.scheduleAckExternal)
} }
setOptionsSnapshot(null) setOptionsSnapshot(null)
setOptionsEditMode(false) setOptionsEditMode(false)
@@ -5460,20 +5455,6 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
</Select> </Select>
<div className="text-[10px] text-muted-foreground mt-1 max-w-xl">{t("vmLxc.ociUpdates.releaseDelayHelp")}</div> <div className="text-[10px] text-muted-foreground mt-1 max-w-xl">{t("vmLxc.ociUpdates.releaseDelayHelp")}</div>
</div> </div>
{ociInstance.host_directories && (
<div className="flex items-start gap-2 text-sm">
<Checkbox
id="oci-ack-external"
checked={scheduleAckExternal}
onCheckedChange={(v) => setScheduleAckExternal(Boolean(v))}
className="mt-0.5"
/>
<Label htmlFor="oci-ack-external" className="leading-tight cursor-pointer">
<span>{t("vmLxc.ociUpdates.ackExternal")}</span>
<div className="text-xs text-muted-foreground mt-0.5">{t("vmLxc.ociUpdates.ackExternalHelp")}</div>
</Label>
</div>
)}
{renderScheduleRunDetails()} {renderScheduleRunDetails()}
</div> </div>
)} )}
@@ -5516,7 +5497,7 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
<button <button
type="button" type="button"
onClick={saveOptionsEdit} onClick={saveOptionsEdit}
disabled={scheduleSaving || (scheduleEnabled && ociInstance.host_directories && !scheduleAckExternal)} disabled={scheduleSaving}
className="h-8 px-3 text-xs rounded-md bg-blue-600 hover:bg-blue-700 text-white transition-colors disabled:opacity-40 inline-flex items-center gap-1.5" className="h-8 px-3 text-xs rounded-md bg-blue-600 hover:bg-blue-700 text-white transition-colors disabled:opacity-40 inline-flex items-center gap-1.5"
> >
{scheduleSaving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Check className="h-3.5 w-3.5" />} {scheduleSaving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Check className="h-3.5 w-3.5" />}
+1 -3
View File
@@ -1206,9 +1206,7 @@
"noKeepBackup": "Das vor dem Update erstellte Backup wird nicht behalten", "noKeepBackup": "Das vor dem Update erstellte Backup wird nicht behalten",
"keepBackupHelp": "Jedes Update sichert den Container, bevor sein Image ersetzt wird, um ihn wiederherzustellen, falls das Update fehlschlägt. Mit dieser Option wird dieses Backup im gewählten Speicher behalten; auf Proxmox Backup Server wird vor dem Update ein Backup geschrieben.", "keepBackupHelp": "Jedes Update sichert den Container, bevor sein Image ersetzt wird, um ihn wiederherzustellen, falls das Update fehlschlägt. Mit dieser Option wird dieses Backup im gewählten Speicher behalten; auf Proxmox Backup Server wird vor dem Update ein Backup geschrieben.",
"scheduleHelp": "Nur das Image wird aktualisiert, und nur wenn seine Registry ein neues veröffentlicht.", "scheduleHelp": "Nur das Image wird aktualisiert, und nur wenn seine Registry ein neues veröffentlicht.",
"releaseDelayHelp": "Ein neues Image wird erst installiert, wenn es mindestens so alt ist.", "releaseDelayHelp": "Ein neues Image wird erst installiert, wenn es mindestens so alt ist."
"ackExternal": "Host-Verzeichnisse werden durch das Backup nicht zurückgesetzt",
"ackExternalHelp": "Dieser Container nutzt Host-Verzeichnisse. Ein geplantes Update läuft nur, wenn dies bestätigt ist."
}, },
"options": { "options": {
"title": "Optionen", "title": "Optionen",
+1 -3
View File
@@ -1227,9 +1227,7 @@
"noKeepBackup": "The backup taken before updating is not kept", "noKeepBackup": "The backup taken before updating is not kept",
"keepBackupHelp": "Every update backs up the container before replacing its image, to restore it if the update fails. With this option that backup is kept in the chosen storage; on Proxmox Backup Server a backup is written before the update.", "keepBackupHelp": "Every update backs up the container before replacing its image, to restore it if the update fails. With this option that backup is kept in the chosen storage; on Proxmox Backup Server a backup is written before the update.",
"scheduleHelp": "Only the image is updated, and only when its registry publishes a new one.", "scheduleHelp": "Only the image is updated, and only when its registry publishes a new one.",
"releaseDelayHelp": "A new image is installed only once it is at least this old.", "releaseDelayHelp": "A new image is installed only once it is at least this old."
"ackExternal": "Host directories are not reverted by the backup",
"ackExternalHelp": "This container uses host directories. A scheduled update runs only with this confirmed."
}, },
"options": { "options": {
"title": "Options", "title": "Options",
+1 -3
View File
@@ -1206,9 +1206,7 @@
"noKeepBackup": "No se conserva el backup realizado antes de actualizar", "noKeepBackup": "No se conserva el backup realizado antes de actualizar",
"keepBackupHelp": "Cada actualización hace un backup del contenedor antes de sustituir su imagen, para restaurarlo si la actualización falla. Con esta opción ese backup se conserva en el almacenamiento elegido; en Proxmox Backup Server se escribe un backup antes de actualizar.", "keepBackupHelp": "Cada actualización hace un backup del contenedor antes de sustituir su imagen, para restaurarlo si la actualización falla. Con esta opción ese backup se conserva en el almacenamiento elegido; en Proxmox Backup Server se escribe un backup antes de actualizar.",
"scheduleHelp": "Solo se actualiza la imagen, y solo cuando su registro publica una nueva.", "scheduleHelp": "Solo se actualiza la imagen, y solo cuando su registro publica una nueva.",
"releaseDelayHelp": "Una imagen nueva solo se instala cuando tiene al menos esta antigüedad.", "releaseDelayHelp": "Una imagen nueva solo se instala cuando tiene al menos esta antigüedad."
"ackExternal": "Los directorios del host no se revierten con el backup",
"ackExternalHelp": "Este contenedor usa directorios del host. Una actualización programada solo se ejecuta con esto confirmado."
}, },
"options": { "options": {
"title": "Opciones", "title": "Opciones",
+1 -3
View File
@@ -1206,9 +1206,7 @@
"noKeepBackup": "La sauvegarde prise avant la mise à jour n'est pas conservée", "noKeepBackup": "La sauvegarde prise avant la mise à jour n'est pas conservée",
"keepBackupHelp": "Chaque mise à jour sauvegarde le conteneur avant de remplacer son image, pour le restaurer si la mise à jour échoue. Avec cette option, cette sauvegarde est conservée dans le stockage choisi ; sur Proxmox Backup Server, une sauvegarde est écrite avant la mise à jour.", "keepBackupHelp": "Chaque mise à jour sauvegarde le conteneur avant de remplacer son image, pour le restaurer si la mise à jour échoue. Avec cette option, cette sauvegarde est conservée dans le stockage choisi ; sur Proxmox Backup Server, une sauvegarde est écrite avant la mise à jour.",
"scheduleHelp": "Seule l'image est mise à jour, et seulement lorsque son registre en publie une nouvelle.", "scheduleHelp": "Seule l'image est mise à jour, et seulement lorsque son registre en publie une nouvelle.",
"releaseDelayHelp": "Une nouvelle image n'est installée qu'une fois qu'elle a au moins cette ancienneté.", "releaseDelayHelp": "Une nouvelle image n'est installée qu'une fois qu'elle a au moins cette ancienneté."
"ackExternal": "Les répertoires de l'hôte ne sont pas rétablis par la sauvegarde",
"ackExternalHelp": "Ce conteneur utilise des répertoires de l'hôte. Une mise à jour planifiée ne s'exécute qu'avec cette confirmation."
}, },
"options": { "options": {
"title": "Possibilités", "title": "Possibilités",
+1 -3
View File
@@ -1206,9 +1206,7 @@
"noKeepBackup": "Il backup eseguito prima dell'aggiornamento non viene conservato", "noKeepBackup": "Il backup eseguito prima dell'aggiornamento non viene conservato",
"keepBackupHelp": "Ogni aggiornamento esegue il backup del container prima di sostituirne l'immagine, per ripristinarlo se l'aggiornamento fallisce. Con questa opzione quel backup viene conservato nello storage scelto; su Proxmox Backup Server viene scritto un backup prima dell'aggiornamento.", "keepBackupHelp": "Ogni aggiornamento esegue il backup del container prima di sostituirne l'immagine, per ripristinarlo se l'aggiornamento fallisce. Con questa opzione quel backup viene conservato nello storage scelto; su Proxmox Backup Server viene scritto un backup prima dell'aggiornamento.",
"scheduleHelp": "Viene aggiornata solo l'immagine, e solo quando il suo registro ne pubblica una nuova.", "scheduleHelp": "Viene aggiornata solo l'immagine, e solo quando il suo registro ne pubblica una nuova.",
"releaseDelayHelp": "Una nuova immagine viene installata solo quando ha almeno questa età.", "releaseDelayHelp": "Una nuova immagine viene installata solo quando ha almeno questa età."
"ackExternal": "Le directory dell'host non vengono ripristinate dal backup",
"ackExternalHelp": "Questo container usa directory dell'host. Un aggiornamento pianificato viene eseguito solo con questa conferma."
}, },
"options": { "options": {
"title": "Opzioni", "title": "Opzioni",
+1 -3
View File
@@ -1206,9 +1206,7 @@
"noKeepBackup": "O backup feito antes da atualização não é mantido", "noKeepBackup": "O backup feito antes da atualização não é mantido",
"keepBackupHelp": "Cada atualização faz um backup do contentor antes de substituir a sua imagem, para o restaurar se a atualização falhar. Com esta opção esse backup é mantido no armazenamento escolhido; no Proxmox Backup Server é escrito um backup antes da atualização.", "keepBackupHelp": "Cada atualização faz um backup do contentor antes de substituir a sua imagem, para o restaurar se a atualização falhar. Com esta opção esse backup é mantido no armazenamento escolhido; no Proxmox Backup Server é escrito um backup antes da atualização.",
"scheduleHelp": "Só a imagem é atualizada, e apenas quando o seu registo publica uma nova.", "scheduleHelp": "Só a imagem é atualizada, e apenas quando o seu registo publica uma nova.",
"releaseDelayHelp": "Uma imagem nova só é instalada quando tem pelo menos esta idade.", "releaseDelayHelp": "Uma imagem nova só é instalada quando tem pelo menos esta idade."
"ackExternal": "Os diretórios do host não são revertidos pelo backup",
"ackExternalHelp": "Este contentor usa diretórios do host. Uma atualização agendada só é executada com esta confirmação."
}, },
"options": { "options": {
"title": "Opções", "title": "Opções",
+1 -3
View File
@@ -1227,9 +1227,7 @@
"noKeepBackup": "Záloha vytvorená pred aktualizáciou sa neponecháva", "noKeepBackup": "Záloha vytvorená pred aktualizáciou sa neponecháva",
"keepBackupHelp": "Každá aktualizácia zálohuje kontajner pred nahradením jeho obrazu, aby ho bolo možné obnoviť, ak aktualizácia zlyhá. S touto možnosťou sa táto záloha ponechá vo zvolenom úložisku; na Proxmox Backup Server sa pred aktualizáciou zapíše záloha.", "keepBackupHelp": "Každá aktualizácia zálohuje kontajner pred nahradením jeho obrazu, aby ho bolo možné obnoviť, ak aktualizácia zlyhá. S touto možnosťou sa táto záloha ponechá vo zvolenom úložisku; na Proxmox Backup Server sa pred aktualizáciou zapíše záloha.",
"scheduleHelp": "Aktualizuje sa iba obraz, a to len keď jeho register zverejní nový.", "scheduleHelp": "Aktualizuje sa iba obraz, a to len keď jeho register zverejní nový.",
"releaseDelayHelp": "Nový obraz sa nainštaluje až vtedy, keď je aspoň takto starý.", "releaseDelayHelp": "Nový obraz sa nainštaluje až vtedy, keď je aspoň takto starý."
"ackExternal": "Adresáre hostiteľa sa zálohou neobnovia",
"ackExternalHelp": "Tento kontajner používa adresáre hostiteľa. Plánovaná aktualizácia sa spustí iba s týmto potvrdením."
}, },
"options": { "options": {
"title": "Možnosti", "title": "Možnosti",
+1 -3
View File
@@ -1206,9 +1206,7 @@
"noKeepBackup": "Säkerhetskopian som tas före uppdateringen behålls inte", "noKeepBackup": "Säkerhetskopian som tas före uppdateringen behålls inte",
"keepBackupHelp": "Varje uppdatering säkerhetskopierar containern innan dess avbild ersätts, för att återställa den om uppdateringen misslyckas. Med det här alternativet behålls säkerhetskopian i den valda lagringen; på Proxmox Backup Server skrivs en säkerhetskopia före uppdateringen.", "keepBackupHelp": "Varje uppdatering säkerhetskopierar containern innan dess avbild ersätts, för att återställa den om uppdateringen misslyckas. Med det här alternativet behålls säkerhetskopian i den valda lagringen; på Proxmox Backup Server skrivs en säkerhetskopia före uppdateringen.",
"scheduleHelp": "Endast avbilden uppdateras, och bara när dess register publicerar en ny.", "scheduleHelp": "Endast avbilden uppdateras, och bara när dess register publicerar en ny.",
"releaseDelayHelp": "En ny avbild installeras först när den är minst så här gammal.", "releaseDelayHelp": "En ny avbild installeras först när den är minst så här gammal."
"ackExternal": "Värdkataloger återställs inte av säkerhetskopian",
"ackExternalHelp": "Den här containern använder värdkataloger. En schemalagd uppdatering körs bara när detta är bekräftat."
}, },
"options": { "options": {
"title": "Alternativ", "title": "Alternativ",
+1 -2
View File
@@ -22272,8 +22272,7 @@ def _run_scheduled_update(vmid: int, sched: dict) -> dict:
storage = (sched.get('backup_storage') or '').strip() storage = (sched.get('backup_storage') or '').strip()
if sched.get('backup') and storage: if sched.get('backup') and storage:
command += ['--keep-backup', storage] command += ['--keep-backup', storage]
if sched.get('acknowledge_external_data'): command.append('--acknowledge-external-data')
command.append('--acknowledge-external-data')
delay = int(sched.get('release_delay_days') or 0) delay = int(sched.get('release_delay_days') or 0)
if delay > 0: if delay > 0:
command += ['--min-image-age-days', str(delay)] command += ['--min-image-age-days', str(delay)]
+6 -12
View File
@@ -237,12 +237,10 @@ def manage_instance(project, ui, row, action=None, lifecycle_args=()):
return False return False
command = [sys.executable, str(project / 'remote/oci_update_current.py'), str(row['vmid']), command = [sys.executable, str(project / 'remote/oci_update_current.py'), str(row['vmid']),
*lifecycle_args] *lifecycle_args]
desired = proposal['candidate'] if proposal else record # Host directories are configured by the user, who knows they are
if any(m['type'] == 'host-bind' for m in desired['deployment'].get('mounts', [])): # outside the container and its backup.
if '--acknowledge-external-data' not in command: if '--acknowledge-external-data' not in command:
if not ui.confirm(translate('Shared host data is not reverted by the backup. Continue?'), False): command.append('--acknowledge-external-data')
return False
command.append('--acknowledge-external-data')
title = translate('Recreate OCI') if proposal else translate('Update OCI') title = translate('Recreate OCI') if proposal else translate('Update OCI')
if proposal is None: if proposal is None:
completed = _run_lifecycle(command, title) completed = _run_lifecycle(command, title)
@@ -388,12 +386,8 @@ def _manage_stack(project, ui, row, action=None, lifecycle_args=()):
command.append('--recover') command.append('--recover')
else: else:
command.extend(lifecycle_args) command.extend(lifecycle_args)
if any(mount['type'] == 'host-bind' for member in members if '--acknowledge-external-data' not in command:
for mount in member.get('deployment', {}).get('mounts', [])): command.append('--acknowledge-external-data')
if '--acknowledge-external-data' not in command:
if not ui.confirm(translate('Shared host data is not reverted by the backups. Continue?'), False):
return False
command.append('--acknowledge-external-data')
completed = _run_lifecycle(command, translate('Recover OCI stack') if pending else translate('Update OCI stack')) completed = _run_lifecycle(command, translate('Recover OCI stack') if pending else translate('Update OCI stack'))
if completed and not pending and not getattr(ui, 'unattended', False): if completed and not pending and not getattr(ui, 'unattended', False):
images.offer_removal(ui, [int(member['vmid']) for member in members]) images.offer_removal(ui, [int(member['vmid']) for member in members])
@@ -149,7 +149,7 @@
{ {
"calloutWarning": { "calloutWarning": {
"title": "Host directories are outside the rollback", "title": "Host directories are outside the rollback",
"body": "The backup covers the rootfs and the container disks it includes. A host directory is not reverted, because other LXCs may use its data. Updating, recreating or recovering an instance with host directories asks for a confirmation of this first." "body": "The backup covers the rootfs and the container disks it includes. A host directory is not reverted, because other LXCs may use its data."
} }
} }
] ]
@@ -133,7 +133,7 @@
], ],
[ [
"Scheduled updates", "Scheduled updates",
"The image is updated at the chosen time only when the registry publishes a new one, and optionally only once it is 1, 3, 7 or 14 days old. A container with changes made outside ProxMenux is skipped and reported; one with host directories runs only when that was confirmed when the schedule was saved." "The image is updated at the chosen time only when the registry publishes a new one, and optionally only once it is 1, 3, 7 or 14 days old. A container with changes made outside ProxMenux is skipped and reported."
] ]
] ]
} }
@@ -149,7 +149,7 @@
{ {
"calloutWarning": { "calloutWarning": {
"title": "Los directorios del host quedan fuera de la restauración", "title": "Los directorios del host quedan fuera de la restauración",
"body": "El backup cubre el rootfs y los discos del contenedor que incluye. Un directorio del host no se revierte, porque otros LXC pueden usar sus datos. Actualizar, recrear o recuperar una instancia con directorios del host pide antes una confirmación de ello." "body": "El backup cubre el rootfs y los discos del contenedor que incluye. Un directorio del host no se revierte, porque otros LXC pueden usar sus datos."
} }
} }
] ]
@@ -133,7 +133,7 @@
], ],
[ [
"Actualizaciones programadas", "Actualizaciones programadas",
"La imagen se actualiza a la hora elegida solo cuando el registro publica una nueva y, opcionalmente, solo cuando tiene 1, 3, 7 o 14 días. Un contenedor con cambios hechos fuera de ProxMenux se salta y se notifica; uno con directorios del host solo se actualiza si se confirmó al guardar la programación." "La imagen se actualiza a la hora elegida solo cuando el registro publica una nueva y, opcionalmente, solo cuando tiene 1, 3, 7 o 14 días. Un contenedor con cambios hechos fuera de ProxMenux se salta y se notifica."
] ]
] ]
} }