@@ -1174,6 +1174,7 @@ def print_usage():
11741174 --detach, -d Run QEMU in background.
11751175 --console, -c Run QEMU in foreground (console mode).
11761176 --builder <ver> Specify a specific vmactions builder version tag.
1177+ --snapshot Enable QEMU snapshot mode (changes are not saved).
11771178 -- Send all following args to the final ssh command (executes inside the VM).
11781179 --help, -h Show this help message.
11791180
@@ -1969,7 +1970,8 @@ def main():
19691970 'enable_ipv6' : False ,
19701971 'debug' : False ,
19711972 'qcow2' : "" ,
1972- 'cachedir' : ""
1973+ 'cachedir' : "" ,
1974+ 'snapshot' : False
19731975 }
19741976
19751977 ssh_passthrough = []
@@ -2072,6 +2074,8 @@ def main():
20722074 elif arg == "--cache-dir" :
20732075 config ['cachedir' ] = os .path .abspath (args [i + 1 ])
20742076 i += 1
2077+ elif arg == "--snapshot" :
2078+ config ['snapshot' ] = True
20752079 i += 1
20762080
20772081 if config ['debug' ]:
@@ -2355,73 +2359,36 @@ def find_image_link(releases, target_zst, target_xz):
23552359 qcow_name += ".qcow2"
23562360
23572361 # Download and Extract
2358- if not os .path .exists (qcow_name ):
2359- if config .get ('cachedir' ):
2360- # New caching strategy: cache qcow2 instead of zst
2361- rel_path = os .path .relpath (output_dir , working_dir )
2362- cache_output_dir = os .path .join (config ['cachedir' ], rel_path )
2363- if not os .path .exists (cache_output_dir ):
2364- debuglog (config ['debug' ], "Creating cache directory: {}" .format (cache_output_dir ))
2365- os .makedirs (cache_output_dir )
2366-
2367- # Check for cached qcow2 file
2368- cached_qcow2 = os .path .join (cache_output_dir , os .path .basename (qcow_name ))
2369-
2370- if os .path .exists (cached_qcow2 ):
2371- # Cache hit: copy qcow2 from cache to data-dir
2372- debuglog (config ['debug' ], "Found cached qcow2: {}" .format (cached_qcow2 ))
2373- log ("Copying cached image: {} -> {}" .format (cached_qcow2 , qcow_name ))
2374- start_time = time .time ()
2375- shutil .copy2 (cached_qcow2 , qcow_name )
2376- duration = time .time () - start_time
2377- debuglog (config ['debug' ], "Copying from cache took {:.2f} seconds" .format (duration ))
2378- else :
2379- # Cache miss: download zst to data-dir, extract, copy qcow2 to cache, delete zst
2380- debuglog (config ['debug' ], "qcow2 not found in cache, downloading zst to data-dir" )
2381-
2382- if not os .path .exists (ova_file ):
2383- if download_file (zst_link , ova_file , config ['debug' ]):
2384- download_optional_parts (zst_link , ova_file , debug = config ['debug' ])
2385-
2386- if not os .path .exists (ova_file ):
2387- fatal ("Failed to download image: " + ova_file )
2388-
2389- # Extract zst/xz to qcow2
2390- log ("Extracting " + ova_file )
2391- extract_start_time = time .time ()
2392- if ova_file .endswith ('.zst' ):
2393- if subprocess .call (['zstd' , '-d' , ova_file , '-o' , qcow_name ]) != 0 :
2394- fatal ("zstd extraction failed" )
2395- elif ova_file .endswith ('.xz' ):
2396- with open (qcow_name , 'wb' ) as f :
2397- if subprocess .call (['xz' , '-d' , '-c' , ova_file ], stdout = f ) != 0 :
2398- fatal ("xz extraction failed" )
2399- extract_duration = time .time () - extract_start_time
2400- debuglog (config ['debug' ], "Extraction took {:.2f} seconds" .format (extract_duration ))
2401-
2402- if not os .path .exists (qcow_name ):
2403- fatal ("Extraction failed" )
2404-
2405- # Delete zst from data-dir immediately after extraction
2406- debuglog (config ['debug' ], "Removing zst file: {}" .format (ova_file ))
2407- try :
2408- os .remove (ova_file )
2409- except OSError as e :
2410- debuglog (config ['debug' ], "Failed to remove zst file: {}" .format (e ))
2411-
2412- # Copy qcow2 to cache
2413- debuglog (config ['debug' ], "Copying qcow2 to cache: {} -> {}" .format (qcow_name , cached_qcow2 ))
2414- log ("Caching extracted image: {}" .format (cached_qcow2 ))
2415- shutil .copy2 (qcow_name , cached_qcow2 )
2362+ cached_qcow2 = None
2363+ if config .get ('cachedir' ):
2364+ rel_path = os .path .relpath (output_dir , working_dir )
2365+ cache_output_dir = os .path .join (config ['cachedir' ], rel_path )
2366+ if not os .path .exists (cache_output_dir ):
2367+ debuglog (config ['debug' ], "Creating cache directory: {}" .format (cache_output_dir ))
2368+ os .makedirs (cache_output_dir )
2369+ cached_qcow2 = os .path .join (cache_output_dir , os .path .basename (qcow_name ))
2370+
2371+ if config ['snapshot' ] and cached_qcow2 and os .path .exists (cached_qcow2 ):
2372+ debuglog (config ['debug' ], "Snapshot mode: Using cached qcow2 directly: {}" .format (cached_qcow2 ))
2373+ qcow_name = cached_qcow2
2374+ elif not os .path .exists (qcow_name ):
2375+ if cached_qcow2 and os .path .exists (cached_qcow2 ):
2376+ # Cache hit: copy qcow2 from cache to data-dir
2377+ debuglog (config ['debug' ], "Found cached qcow2: {}" .format (cached_qcow2 ))
2378+ log ("Copying cached image: {} -> {}" .format (cached_qcow2 , qcow_name ))
2379+ start_time = time .time ()
2380+ shutil .copy2 (cached_qcow2 , qcow_name )
2381+ duration = time .time () - start_time
2382+ debuglog (config ['debug' ], "Copying from cache took {:.2f} seconds" .format (duration ))
24162383 else :
2417- # No cache-dir: download zst, extract, delete zst
2384+ # Cache miss or no cache-dir: download and extract
24182385 if not os .path .exists (ova_file ):
24192386 if download_file (zst_link , ova_file , config ['debug' ]):
24202387 download_optional_parts (zst_link , ova_file , debug = config ['debug' ])
24212388
24222389 if not os .path .exists (ova_file ):
2423- fatal ("Image file not found : " + ova_file )
2424-
2390+ fatal ("Failed to download image : " + ova_file )
2391+
24252392 log ("Extracting " + ova_file )
24262393 extract_start_time = time .time ()
24272394 if ova_file .endswith ('.zst' ):
@@ -2442,6 +2409,19 @@ def find_image_link(releases, target_zst, target_xz):
24422409 os .remove (ova_file )
24432410 except OSError :
24442411 pass
2412+
2413+ if cached_qcow2 :
2414+ # Copy qcow2 to cache
2415+ debuglog (config ['debug' ], "Copying qcow2 to cache: {} -> {}" .format (qcow_name , cached_qcow2 ))
2416+ log ("Caching extracted image: {}" .format (cached_qcow2 ))
2417+ shutil .copy2 (qcow_name , cached_qcow2 )
2418+ if config ['snapshot' ]:
2419+ debuglog (config ['debug' ], "Snapshot mode: Removing extracted qcow2 from data-dir and using cache" )
2420+ try :
2421+ os .remove (qcow_name )
2422+ except OSError :
2423+ pass
2424+ qcow_name = cached_qcow2
24452425
24462426 # Key files
24472427 vm_name = "{}-{}" .format (config ['os' ], config ['release' ])
@@ -2587,6 +2567,9 @@ def find_image_link(releases, target_zst, target_xz):
25872567 "-drive" , "file={},format=qcow2,if={}" .format (qcow_name , disk_if )
25882568 ])
25892569
2570+ if config ['snapshot' ]:
2571+ args_qemu .append ("-snapshot" )
2572+
25902573 # Windows on ARM has DirectSound issues; disable audio only there.
25912574 if IS_WINDOWS and host_arch == "aarch64" :
25922575 args_qemu .extend (["-audiodev" , "none,id=snd" ])
0 commit comments