fix: resolve datastore URL encoding errors and improve snapshot removal reliability with fresh object lookups

This commit is contained in:
Rizqi 2026-06-22 01:41:18 +07:00
parent 2b5514f4c2
commit 9621175a76

View File

@ -139,14 +139,17 @@ def find_datacenter_for_datastore(content, datastore_name):
def download_datastore_file(host, dc_name, datastore_name, ds_path, local_path, def download_datastore_file(host, dc_name, datastore_name, ds_path, local_path,
session_cookie, verify_ssl=True, progress_cb=None): session_cookie, verify_ssl=True, progress_cb=None):
"""Download a file from a vSphere datastore. progress_cb(bytes_done, bytes_total) is optional.""" """Download a file from a vSphere datastore. progress_cb(bytes_done, bytes_total) is optional."""
encoded_path = urllib.parse.quote(ds_path, safe='') # Keep slashes unencoded (safe='/') — vCenter's /folder/ API requires them in the URL path.
encoded_path = urllib.parse.quote(ds_path, safe='/')
url = (f"https://{host}/folder/{encoded_path}" url = (f"https://{host}/folder/{encoded_path}"
f"?dcPath={urllib.parse.quote(dc_name)}&dsName={urllib.parse.quote(datastore_name)}") f"?dcPath={urllib.parse.quote(dc_name)}&dsName={urllib.parse.quote(datastore_name)}")
headers = {"Cookie": f"vmware_soap_session={session_cookie}"} headers = {"Cookie": f"vmware_soap_session={session_cookie}"}
print(f"Downloading {ds_path} from datastore {datastore_name} to {local_path}") print(f"Downloading {ds_path} from datastore {datastore_name} to {local_path}")
print(f" URL: {url}")
with requests.get(url, headers=headers, stream=True, verify=verify_ssl, proxies={"http": None, "https": None}) as r: with requests.get(url, headers=headers, stream=True, verify=verify_ssl, proxies={"http": None, "https": None}) as r:
r.raise_for_status() r.raise_for_status()
total_bytes = int(r.headers.get('Content-Length', 0)) total_bytes = int(r.headers.get('Content-Length', 0))
print(f" HTTP {r.status_code}, Content-Length: {total_bytes} bytes")
done_bytes = 0 done_bytes = 0
os.makedirs(os.path.dirname(local_path), exist_ok=True) os.makedirs(os.path.dirname(local_path), exist_ok=True)
with open(local_path, 'wb') as f: with open(local_path, 'wb') as f:
@ -406,16 +409,28 @@ def _run_backup_impl(host, user, password, vm_name, dest, compress, no_verify_ss
print('Backup completed successfully') print('Backup completed successfully')
finally: finally:
if created_snapshot: if created_snapshot:
snap_root = getattr(vm, 'snapshot', None) try:
if snap_root and snap_root.rootSnapshotList: # Re-fetch vm snapshot state to avoid stale object references
snap_obj = find_snapshot_by_name(snap_root.rootSnapshotList, snap_name) content.rootFolder # touch to keep session alive
if snap_obj: vm_fresh = None
try: obj_view2 = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True)
for v in obj_view2.view:
if v.name == vm_name:
vm_fresh = v
break
obj_view2.Destroy()
target_vm = vm_fresh or vm
snap_root = getattr(target_vm, 'snapshot', None)
if snap_root and snap_root.rootSnapshotList:
snap_obj = find_snapshot_by_name(snap_root.rootSnapshotList, snap_name)
if snap_obj:
remove_snapshot(snap_obj) remove_snapshot(snap_obj)
except Exception as e: else:
print(f'Failed to remove snapshot: {e}', file=sys.stderr) print('Snapshot already removed or not found in tree')
else: else:
print('Snapshot object not found in tree; may have been removed already') print('No snapshots found on VM — may have already been removed')
except Exception as e:
print(f'Failed to remove snapshot: {e}', file=sys.stderr)
_prog('done', 100, 'Backup finished successfully') _prog('done', 100, 'Backup finished successfully')
finally: finally:
if si: if si: