Skip to content

Commit a1b2a13

Browse files
authored
Merge pull request #657 from xcp-ng/gln/tools-clean-improvements-xyzl
tools clean: clean snapshots and continue on error
2 parents 80e23a7 + 56d3e2b commit a1b2a13

2 files changed

Lines changed: 35 additions & 13 deletions

File tree

lib/tools/cli.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ def _command_update(args: argparse.Namespace) -> None:
2424
update_pools(inventory, reboot=args.reboot, parallel=args.parallel)
2525

2626

27-
def _command_clean(args: argparse.Namespace) -> None:
27+
def _command_clean(args: argparse.Namespace) -> int:
2828
if args.inventory:
2929
inventory = load_inventory(args.inventory)
3030
else:
3131
inventory = into_inventory(args.hosts, [], args.hosting_pool)
3232

33-
clean_pools(inventory, dry_run=args.dry_run)
33+
return clean_pools(inventory, dry_run=args.dry_run)
3434

3535

3636
def _command_exec(args: argparse.Namespace) -> int:
@@ -105,8 +105,8 @@ def cli() -> None:
105105
# subparser - command: clean
106106
subparser_cmd_clean = subparsers.add_parser(
107107
name="clean",
108-
description="Remove all VMs and all VDIs on local storage from target pools",
109-
help="Remove all VMs and all VDIs on local storage from target pools",
108+
description="Remove all VMs, snapshorts and VDIs on local storage from target pools",
109+
help="Remove all VMs, snapshorts and VDIs on local storage from target pools",
110110
)
111111
cmd_clean_excl_grp = subparser_cmd_clean.add_mutually_exclusive_group(required=True)
112112
cmd_clean_excl_grp.add_argument(

lib/tools/tasks/clean.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from .. import logger
1818

19-
def clean_pools(inventory: Inventory, dry_run: bool = False) -> None:
19+
def clean_pools(inventory: Inventory, dry_run: bool = False) -> int:
2020
"""Remove all VMs and all orphan VDIs on local storage from pool(s).
2121
2222
.. note::
@@ -31,6 +31,8 @@ def clean_pools(inventory: Inventory, dry_run: bool = False) -> None:
3131
Each host (key) holds its own config data (values, eg: `enablerepos`).
3232
:param bool dry_run:
3333
When True, only log what would be removed without actually deleting.
34+
:return:
35+
The number of VMs/VDIs/snapshots that failed to be removed.
3436
"""
3537
inventory_hosts = inventory["hosts"]
3638
pools: list[Pool] = []
@@ -42,44 +44,64 @@ def clean_pools(inventory: Inventory, dry_run: bool = False) -> None:
4244

4345
with ThreadPoolExecutor() as executor:
4446
futures = {executor.submit(clean_pool, p, dry_run): p for p in pools}
47+
failures = 0
4548
for future in futures:
4649
pool = futures[future]
4750
try:
48-
future.result()
51+
failures += future.result()
4952
except Exception as exc:
50-
logger.error(f"Cleaning pool has failed! The master {pool.master} cannot be cleaned.")
51-
raise exc
53+
logger.error(f"Cleaning pool has failed! The master {pool.master} cannot be cleaned: {exc}")
54+
failures += 1
55+
return failures
5256

53-
def clean_pool(pool: Pool, dry_run: bool) -> None:
54-
"""Remove all VMs and all orphan VDIs on local SRs from a single pool."""
57+
def clean_pool(pool: Pool, dry_run: bool) -> int:
58+
"""Remove all VMs and all orphan VDIs on local SRs from a single pool.
59+
60+
:return:
61+
The number of VMs plus number VDIs that failed to be removed from pool
62+
"""
5563
master = pool.master
5664
log_prefix = 'Would remove' if dry_run else 'Removing'
65+
failures = 0
5766

5867
vm_uuids = safe_split(master.xe(
5968
'vm-list',
6069
{'is-control-domain': False, 'is-a-template': False},
6170
minimal=True,
71+
)) + safe_split(master.xe(
72+
'vm-list',
73+
{'is-a-snapshot': True},
74+
minimal=True,
6275
))
6376
for vm_uuid in vm_uuids:
6477
vm = VM(vm_uuid, master)
6578
logger.info(f"[{master}] {log_prefix} VM {vm.uuid} ({vm.name()})")
6679
if not dry_run:
67-
vm.destroy(verify=True)
80+
try:
81+
vm.destroy(verify=True)
82+
except Exception as exc:
83+
logger.error(f"[{master}] Failed to remove VM {vm.uuid} ({vm.name()}): {exc}")
84+
failures += 1
6885

6986
sr_uuids = local_sr_uuids(pool)
7087
for sr_uuid in sr_uuids:
7188
for vdi_uuid in SR(sr_uuid, pool).vdi_uuids(managed=True):
7289
vdi = VDI(vdi_uuid, sr=SR(sr_uuid, pool))
7390
logger.info(f"[{master}] {log_prefix} orphan VDI {vdi.uuid} from local SR {sr_uuid}")
7491
if not dry_run:
75-
vdi.destroy()
92+
try:
93+
vdi.destroy()
94+
except Exception as exc:
95+
logger.error(f"[{master}] Failed to destroy VDI {vdi.uuid}: {exc}")
96+
failures += 1
7697

77-
if not dry_run:
98+
if not dry_run and failures == 0:
7899
for sr_uuid in sr_uuids:
79100
wait_for_not(
80101
lambda: len(SR(sr_uuid, pool).vdi_uuids(managed=True)) > 0,
81102
f"Wait for local SR {sr_uuid} to be empty",
82103
)
104+
return failures
83105

84106
def local_sr_uuids(pool: Pool) -> list[str]:
85107
"""Return the UUIDs of the pool's local (non-shared, user) SRs."""

0 commit comments

Comments
 (0)