@@ -12,6 +12,7 @@ use std::process::Command;
1212use  crate :: { 
1313    get_bck_command,  get_test_image,  run_bcvk,  run_bcvk_nocapture,  LIBVIRT_INTEGRATION_TEST_LABEL , 
1414} ; 
15+ use  bcvk:: xml_utils:: parse_xml_dom; 
1516
1617/// Test libvirt list functionality (lists domains) 
1718pub  fn  test_libvirt_list_functionality ( )  { 
@@ -941,3 +942,157 @@ pub fn test_libvirt_error_handling() {
941942
942943    println ! ( "libvirt error handling validated" ) ; 
943944} 
945+ 
946+ /// Test transient VM functionality 
947+ pub  fn  test_libvirt_transient_vm ( )  { 
948+     let  test_image = get_test_image ( ) ; 
949+ 
950+     // Generate unique domain name for this test 
951+     let  domain_name = format ! ( 
952+         "test-transient-{}" , 
953+         std:: time:: SystemTime :: now( ) 
954+             . duration_since( std:: time:: UNIX_EPOCH ) 
955+             . unwrap( ) 
956+             . as_secs( ) 
957+     ) ; 
958+ 
959+     println ! ( "Testing transient VM with domain: {}" ,  domain_name) ; 
960+ 
961+     // Cleanup any existing domain with this name 
962+     cleanup_domain ( & domain_name) ; 
963+ 
964+     // Create transient domain 
965+     println ! ( "Creating transient libvirt domain..." ) ; 
966+     let  create_output = run_bcvk ( & [ 
967+         "libvirt" , 
968+         "run" , 
969+         "--name" , 
970+         & domain_name, 
971+         "--label" , 
972+         LIBVIRT_INTEGRATION_TEST_LABEL , 
973+         "--transient" , 
974+         "--filesystem" , 
975+         "ext4" , 
976+         & test_image, 
977+     ] ) 
978+     . expect ( "Failed to run libvirt run with --transient" ) ; 
979+ 
980+     println ! ( "Create stdout: {}" ,  create_output. stdout) ; 
981+     println ! ( "Create stderr: {}" ,  create_output. stderr) ; 
982+ 
983+     if  !create_output. success ( )  { 
984+         cleanup_domain ( & domain_name) ; 
985+         panic ! ( 
986+             "Failed to create transient domain: {}" , 
987+             create_output. stderr
988+         ) ; 
989+     } 
990+ 
991+     println ! ( "Successfully created transient domain: {}" ,  domain_name) ; 
992+ 
993+     // Verify domain is transient using virsh dominfo 
994+     println ! ( "Verifying domain is marked as transient..." ) ; 
995+     let  dominfo_output = Command :: new ( "virsh" ) 
996+         . args ( & [ "dominfo" ,  & domain_name] ) 
997+         . output ( ) 
998+         . expect ( "Failed to run virsh dominfo" ) ; 
999+ 
1000+     if  !dominfo_output. status . success ( )  { 
1001+         cleanup_domain ( & domain_name) ; 
1002+         let  stderr = String :: from_utf8_lossy ( & dominfo_output. stderr ) ; 
1003+         panic ! ( "Failed to get domain info: {}" ,  stderr) ; 
1004+     } 
1005+ 
1006+     let  dominfo = String :: from_utf8_lossy ( & dominfo_output. stdout ) ; 
1007+     println ! ( "Domain info:\n {}" ,  dominfo) ; 
1008+ 
1009+     // Verify "Persistent: no" appears in dominfo 
1010+     assert ! ( 
1011+         dominfo. contains( "Persistent:" )  && dominfo. contains( "no" ) , 
1012+         "Domain should be marked as non-persistent (transient). dominfo: {}" , 
1013+         dominfo
1014+     ) ; 
1015+     println ! ( "✓ Domain is correctly marked as transient (Persistent: no)" ) ; 
1016+ 
1017+     // Verify domain XML contains transient disk element 
1018+     println ! ( "Checking domain XML for transient disk configuration..." ) ; 
1019+     let  dumpxml_output = Command :: new ( "virsh" ) 
1020+         . args ( & [ "dumpxml" ,  & domain_name] ) 
1021+         . output ( ) 
1022+         . expect ( "Failed to dump domain XML" ) ; 
1023+ 
1024+     let  domain_xml = String :: from_utf8_lossy ( & dumpxml_output. stdout ) ; 
1025+ 
1026+     // Parse the XML properly using our XML parser 
1027+     let  xml_dom = parse_xml_dom ( & domain_xml) . expect ( "Failed to parse domain XML" ) ; 
1028+ 
1029+     // Verify domain XML contains transient disk element 
1030+     let  has_transient = xml_dom. find ( "transient" ) . is_some ( ) ; 
1031+     assert ! ( 
1032+         has_transient, 
1033+         "Domain XML should contain transient disk element" 
1034+     ) ; 
1035+     println ! ( "✓ Domain XML contains transient disk element" ) ; 
1036+ 
1037+     // Extract the base disk path from the domain XML using proper XML parsing 
1038+     let  base_disk_path = xml_dom
1039+         . find ( "source" ) 
1040+         . and_then ( |source_node| source_node. attributes . get ( "file" ) ) 
1041+         . map ( |s| s. to_string ( ) ) ; 
1042+ 
1043+     println ! ( "Base disk path: {:?}" ,  base_disk_path) ; 
1044+ 
1045+     // Stop the domain (this should make it disappear since it's transient) 
1046+     println ! ( "Stopping transient domain (should disappear)..." ) ; 
1047+     let  destroy_output = Command :: new ( "virsh" ) 
1048+         . args ( & [ "destroy" ,  & domain_name] ) 
1049+         . output ( ) 
1050+         . expect ( "Failed to run virsh destroy" ) ; 
1051+ 
1052+     if  !destroy_output. status . success ( )  { 
1053+         let  stderr = String :: from_utf8_lossy ( & destroy_output. stderr ) ; 
1054+         panic ! ( "Failed to stop domain: {}" ,  stderr) ; 
1055+     } 
1056+ 
1057+     // Poll for domain disappearance with timeout 
1058+     println ! ( "Verifying domain has disappeared..." ) ; 
1059+     let  start_time = std:: time:: Instant :: now ( ) ; 
1060+     let  timeout = std:: time:: Duration :: from_secs ( 10 ) ; 
1061+     let  mut  domain_disappeared = false ; 
1062+ 
1063+     while  start_time. elapsed ( )  < timeout { 
1064+         let  list_output = Command :: new ( "virsh" ) 
1065+             . args ( & [ "list" ,  "--all" ,  "--name" ] ) 
1066+             . output ( ) 
1067+             . expect ( "Failed to list domains" ) ; 
1068+ 
1069+         let  domain_list = String :: from_utf8_lossy ( & list_output. stdout ) ; 
1070+         if  !domain_list. contains ( & domain_name)  { 
1071+             domain_disappeared = true ; 
1072+             break ; 
1073+         } 
1074+ 
1075+         // Wait briefly before checking again 
1076+         std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 200 ) ) ; 
1077+     } 
1078+ 
1079+     assert ! ( 
1080+         domain_disappeared, 
1081+         "Transient domain should have disappeared after shutdown within {} seconds" , 
1082+         timeout. as_secs( ) 
1083+     ) ; 
1084+     println ! ( "✓ Transient domain disappeared after shutdown" ) ; 
1085+ 
1086+     // Verify base disk still exists (only the overlay was removed) 
1087+     if  let  Some ( ref  disk_path)  = base_disk_path { 
1088+         println ! ( "Verifying base disk still exists: {}" ,  disk_path) ; 
1089+         let  disk_exists = std:: path:: Path :: new ( disk_path) . exists ( ) ; 
1090+         assert ! ( 
1091+             disk_exists, 
1092+             "Base disk should still exist after transient domain shutdown" 
1093+         ) ; 
1094+         println ! ( "✓ Base disk still exists (not deleted)" ) ; 
1095+     } 
1096+ 
1097+     println ! ( "✓ Transient VM test passed" ) ; 
1098+ } 
0 commit comments