@@ -305,6 +305,7 @@ def init_modules():
305305
306306 # Import machine manager.
307307 import_plugin (f"modules.machinery.{ cuckoo .cuckoo .machinery } " )
308+ check_snapshot_state ()
308309
309310 for category , entries in list_plugins ().items ():
310311 log .debug ('Imported "%s" modules:' , category )
@@ -316,6 +317,94 @@ def init_modules():
316317 log .debug ("\t |-- %s" , entry .__name__ )
317318
318319
320+ def check_snapshot_state ():
321+ """Checks the state of snapshots and machine architecture for KVM/QEMU machinery."""
322+ if cuckoo .cuckoo .machinery not in ("kvm" , "qemu" ):
323+ return
324+
325+ try :
326+ import libvirt
327+ from xml .etree import ElementTree
328+ except ImportError :
329+ raise CuckooStartupError (
330+ "The 'libvirt-python' library is required for KVM/QEMU machinery but is not installed. "
331+ "Please install it (e.g., 'cd /opt/CAPEv2/ ; sudo -u cape /etc/poetry/bin/poetry run extra/libvirt_installer.sh')."
332+ )
333+
334+ machinery_config = Config (cuckoo .cuckoo .machinery )
335+ dsn = machinery_config .get (cuckoo .cuckoo .machinery ).get ("dsn" )
336+ conn = None
337+
338+ try :
339+ conn = libvirt .open (dsn )
340+ except libvirt .libvirtError as e :
341+ raise CuckooStartupError (f"Failed to connect to libvirt with DSN '{ dsn } '. Error: { e } " )
342+
343+ if conn is None :
344+ raise CuckooStartupError (f"Failed to connect to libvirt with DSN '{ dsn } '. Please check your configuration and libvirt service." )
345+
346+ try :
347+ for machine_name in machinery_config .get (cuckoo .cuckoo .machinery ).machines .split ("," ):
348+ machine_name = machine_name .strip ()
349+ if not machine_name :
350+ continue
351+
352+ try :
353+ domain = conn .lookupByName (machine_name )
354+ machine_config = machinery_config .get (machine_name )
355+
356+ # Check for valid architecture configuration.
357+ arch = machine_config .get ("arch" )
358+ if not arch :
359+ raise CuckooStartupError (f"Missing 'arch' configuration for VM '{ machine_name } '. Please specify a valid architecture (e.g., x86, x64)." )
360+
361+ if arch == "x86_64" :
362+ raise CuckooStartupError (
363+ f"Invalid architecture '{ arch } ' for VM '{ machine_name } '. Please use 'x64' instead of 'x86_64'."
364+ )
365+
366+ if arch != arch .lower ():
367+ raise CuckooStartupError (
368+ f"Invalid architecture '{ arch } ' for VM '{ machine_name } '. Architecture must be all lowercase."
369+ )
370+
371+ # Check snapshot state.
372+ snapshot_name = machine_config .get ("snapshot" )
373+ snapshot = None
374+
375+ if snapshot_name :
376+ snapshot = domain .snapshotLookupByName (snapshot_name )
377+ else :
378+ if domain .hasCurrentSnapshot (0 ):
379+ snapshot = domain .snapshotCurrent (0 )
380+ snapshot_name = snapshot .getName ()
381+ log .info ("No snapshot name configured for VM '%s', checking latest: '%s'" , machine_name , snapshot_name )
382+ else :
383+ log .warning ("No snapshot configured or found for VM '%s'. Skipping check." , machine_name )
384+ continue
385+
386+ xml_desc = snapshot .getXMLDesc (0 )
387+ root = ElementTree .fromstring (xml_desc )
388+ state_element = root .find ("state" )
389+
390+ if state_element is None or state_element .text != "running" :
391+ state = state_element .text if state_element is not None else "unknown"
392+ raise CuckooStartupError (
393+ f"Snapshot '{ snapshot_name } ' for VM '{ machine_name } ' is not in a 'running' state (current state: '{ state } '). "
394+ "Please ensure you take snapshots of running VMs."
395+ )
396+
397+ except libvirt .libvirtError as e :
398+ # It's possible a snapshot name is provided but doesn't exist, which is a config error.
399+ snapshot_identifier = f"with snapshot '{ snapshot_name } '" if snapshot_name else ""
400+ raise CuckooStartupError (
401+ f"Error checking snapshot state for VM '{ machine_name } ' { snapshot_identifier } . Libvirt error: { e } "
402+ )
403+ finally :
404+ if conn :
405+ conn .close ()
406+
407+
319408def init_rooter ():
320409 """If required, check whether the rooter is running and whether we can
321410 connect to it."""
0 commit comments