mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 19:56:48 +00:00
fix(migrate): a copied disk's source file is deleted only when no compute may use it
The source host alone was asked: a file on storage the computes share, used by a VM or volume of another compute, was deleted under it. The check the disk move makes, every compute and a host that cannot tell counting as using it, moves to instances.utils and serves both. The destination is not asked when its copy has the source's path: its VM uses that copy, another file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
This commit is contained in:
parent
c2c255a866
commit
826eaac813
6 changed files with 66 additions and 54 deletions
|
|
@ -2,6 +2,7 @@ import os
|
|||
import random
|
||||
import re
|
||||
import string
|
||||
import threading
|
||||
from collections import namedtuple
|
||||
|
||||
from accounts.models import UserInstance, UserAttributes
|
||||
|
|
@ -70,6 +71,35 @@ def paths_used_on_other_computes(compute, own):
|
|||
return used & set(own)
|
||||
|
||||
|
||||
USED_CHECK_WAIT = 60 # seconds a disk move or a migration waits for the computes to tell whether its old file is used
|
||||
|
||||
|
||||
def used_anywhere(path, skip=None):
|
||||
"""Whether a VM or volume of any compute uses path (paths_in_use), as
|
||||
shared storage holds it for every host that mounts it; a compute that
|
||||
cannot be read might, and so might one that does not answer within
|
||||
USED_CHECK_WAIT: libvirt has no call timeout, and a stuck host would
|
||||
hold the job for as long as it is stuck. skip: a compute not asked."""
|
||||
computes = list(Compute.objects.exclude(pk=skip))
|
||||
answer = []
|
||||
|
||||
def check():
|
||||
for c in computes:
|
||||
try:
|
||||
if path in wvmConnect(c.hostname, c.login, c.password, c.type).paths_in_use():
|
||||
answer.append(True)
|
||||
return
|
||||
except libvirtError:
|
||||
answer.append(True)
|
||||
return
|
||||
answer.append(False)
|
||||
|
||||
thread = threading.Thread(target=check, daemon=True)
|
||||
thread.start()
|
||||
thread.join(USED_CHECK_WAIT)
|
||||
return answer[0] if answer else True
|
||||
|
||||
|
||||
def disk_gib(disks):
|
||||
"""The size of the disks in whole GiB each, as the quota counts them."""
|
||||
return sum(int(disk["size"] or 0) >> 30 for disk in disks)
|
||||
|
|
@ -225,8 +255,9 @@ def migrate_instance(
|
|||
used, or the error), then each later step that failed in a row of its
|
||||
own; on_moved is called once the outcome's row is written. disk_pools:
|
||||
{target: pool} of the disks copied into pools of new_compute (moveto's
|
||||
pools); their source files go with delete_source unless another VM or
|
||||
volume uses them, and are named in the log otherwise. Returns the mode."""
|
||||
pools); their source files go with delete_source unless a VM or volume
|
||||
of any compute may use them (used_anywhere), and are named in the log
|
||||
otherwise. Returns the mode."""
|
||||
source, target = instance.compute, migration_target(new_compute)
|
||||
|
||||
def log(action, error=None):
|
||||
|
|
@ -285,16 +316,12 @@ def migrate_instance(
|
|||
if conn_new is not None:
|
||||
conn_new.close()
|
||||
|
||||
in_use = set()
|
||||
if copied and delete_source:
|
||||
try:
|
||||
in_use = instance.proxy.paths_in_use()
|
||||
except libvirtError as error:
|
||||
log("check the source copies before deleting them", error)
|
||||
delete_source = False
|
||||
for path in copied:
|
||||
if not delete_source or path in in_use:
|
||||
log(f"keep the source copy {path}" + (": another VM or volume uses it" if path in in_use else ""))
|
||||
for path, copy in copied:
|
||||
# shared storage: a VM or volume of any compute may use it; the destination's
|
||||
# VM uses the copy, another file when it has the same path there
|
||||
used = delete_source and used_anywhere(path, skip=new_compute.pk if copy == path else None)
|
||||
if not delete_source or used:
|
||||
log(f"keep the source copy {path}" + (": a VM or volume of a compute may use it" if used else ""))
|
||||
continue
|
||||
try:
|
||||
instance.proxy.get_volume_by_path(path).delete(0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue