mirror of
https://github.com/xcp-ng/xcp.git
synced 2026-10-12 02:24:43 +00:00
A regression was introduced to VM.revert in xapi-26.1.4-3.3, which left VBDs
attached to the VM when they are not present in the reverted-to
snapshot (instead of destroying such VBDs, as was done before).
VMs that were reverted to their snapshots after xapi-26.1.4-3.3 may have
such "leftover" VBDs. This script can be used to detect possible cases in
pools. Sadly, there is no way to automatically distinguish a VBD that
was created after a VM.revert from a VBD that survived the VM.revert,
so this script relies on the user to determine if the suspicious VBDs are
indeed affected or not.
Therefore, tbis is not a destructive script, it just uses the API to find the
affected VBDs and presents them to the user, as in this example output:
VM e2678368-596d-1b6e-7211-9ee45f60dec1 ("Alpine minimal for CI") has
VBDs that are not present in the snapshot it was reverted to
(303ead66-9515-b1c8-0dc5-d33b7644b161 "Alpine minimal for CI_snapshot1"):
xvdb (ca8610d2-76e3-6c54-4378-ee599619b4bb))
The script follows this algorithm:
1. Find all VMs that are children of snapshots
(i.e. were reverted to a particular snapshot at some point in time)
2. Find VBDs that are currently present in such VMs but are
not present in the snapshot
Signed-off-by: Andrii Sultanov <[email protected]>
77 lines
3.1 KiB
Python
77 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import XenAPI
|
|
|
|
def main():
|
|
session = XenAPI.xapi_local()
|
|
xapi = session.xenapi
|
|
try:
|
|
xapi.login_with_password("root", "", "0.1", "leaked_vbds_script")
|
|
# Get a list of snapshots
|
|
vms = xapi.VM.get_all_records()
|
|
snap_vms = {ref: vm for ref, vm in vms.items() if vm['is_a_snapshot']}
|
|
nonsnap_vms = {ref: vm for ref, vm in vms.items() if not vm['is_a_snapshot']}
|
|
|
|
# Dict of {vm: last_snapshot}
|
|
vm_snaps = {vm_ref: vm['parent'] for (vm_ref, vm) in nonsnap_vms.items() if
|
|
vm['parent'] != 'OpaqueRef:NULL' and
|
|
not vm['parent'].startswith('Ref:')}
|
|
|
|
# Find the snapshots the VM could have been reverted to
|
|
vm_reverted_snaps = {}
|
|
for vm_ref, snap in vm_snaps.items():
|
|
snaps = [snap]
|
|
while True:
|
|
new_snap = xapi.VM.get_parent(snap)
|
|
|
|
if new_snap.startswith('Ref:'):
|
|
new_snap = next((vm_ref for (vm_ref, vm) in snap_vms.items()
|
|
if vm['children'] and vm['children'][0] == snap),
|
|
'OpaqueRef:NULL')
|
|
if new_snap == 'OpaqueRef:NULL':
|
|
break
|
|
snaps.append(new_snap)
|
|
snap = new_snap
|
|
|
|
vm_uuid = xapi.VM.get_uuid(vm_ref)
|
|
|
|
# VM keeps the snapshot's install time
|
|
install_time = xapi.VM_metrics.get_install_time(xapi.VM.get_metrics(vm_ref))
|
|
snaps = [snap for snap in snaps if
|
|
install_time == xapi.VM_metrics.get_install_time(xapi.VM.get_metrics(snap))]
|
|
|
|
if snaps:
|
|
vm_reverted_snaps[vm_ref] = snaps
|
|
|
|
for vm, snapshots in vm_reverted_snaps.items():
|
|
# List of VBDs that are attached to VMs
|
|
vm_vbds = xapi.VM.get_VBDs(vm)
|
|
vm_vbds = {xapi.VBD.get_device(vbd_ref): vbd_ref for vbd_ref in vm_vbds}
|
|
vm_set = set(vm_vbds)
|
|
vm_uuid = xapi.VM.get_uuid(vm)
|
|
|
|
for snap in snapshots:
|
|
# List of VBDs that are attached to snapshots
|
|
snapshotted_vbds = xapi.VM.get_VBDs(snap)
|
|
snapshotted_vbds = {xapi.VBD.get_device(vbd_ref): vbd_ref for vbd_ref in snapshotted_vbds}
|
|
snap_set = set(snapshotted_vbds)
|
|
|
|
# List of VBDs that are not part of the snapshotted VM but
|
|
# are attached to the VM currently
|
|
diff = set(vm_vbds) - snap_set
|
|
if diff:
|
|
vm_uuid = xapi.VM.get_uuid(vm)
|
|
vm_name_label = xapi.VM.get_name_label(vm)
|
|
snap_uuid = xapi.VM.get_uuid(snap)
|
|
snap_name_label = xapi.VM.get_name_label(snap)
|
|
vbds = ','.join([f'{dev} ({xapi.VBD.get_uuid(vm_vbds[dev])})' for dev in diff])
|
|
print(f'VM {vm_uuid} ("{vm_name_label}") has VBDs that are'
|
|
f' not present in the snapshot it was reverted to ({snap_uuid}'
|
|
f' "{snap_name_label}"): {vbds}')
|
|
|
|
finally:
|
|
session.xenapi.session.logout()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|