@@ -43,7 +43,7 @@ pub struct DomainBuilder {
4343 disk_path : Option < String > ,
4444 transient_disk : bool , // Use transient disk with temporary overlay
4545 network : Option < String > ,
46- vnc_port : Option < u16 > ,
46+ graphical_console : bool ,
4747 kernel_args : Option < String > ,
4848 metadata : HashMap < String , String > ,
4949 qemu_args : Vec < String > ,
@@ -78,7 +78,7 @@ impl DomainBuilder {
7878 disk_path : None ,
7979 transient_disk : false ,
8080 network : None ,
81- vnc_port : None ,
81+ graphical_console : false ,
8282 kernel_args : None ,
8383 metadata : HashMap :: new ( ) ,
8484 qemu_args : Vec :: new ( ) ,
@@ -133,10 +133,9 @@ impl DomainBuilder {
133133 self
134134 }
135135
136- /// Enable VNC on specified port
137- #[ allow( dead_code) ]
138- pub fn with_vnc ( mut self , port : u16 ) -> Self {
139- self . vnc_port = Some ( port) ;
136+ /// Enable graphical console (SPICE) for virt-manager access
137+ pub fn with_graphical_console ( mut self ) -> Self {
138+ self . graphical_console = true ;
140139 self
141140 }
142141
@@ -498,19 +497,23 @@ impl DomainBuilder {
498497 }
499498 }
500499
501- // VNC graphics if enabled
502- if let Some ( vnc_port) = self . vnc_port {
500+ // Graphical console (SPICE) for virt-manager access
501+ if self . graphical_console {
502+ writer. start_element ( "graphics" , & [ ( "type" , "spice" ) , ( "autoport" , "yes" ) ] ) ?;
503+ writer. write_empty_element ( "listen" , & [ ( "type" , "address" ) ] ) ?;
504+ writer. end_element ( "graphics" ) ?;
505+ writer. start_element ( "video" , & [ ] ) ?;
503506 writer. write_empty_element (
504- "graphics" ,
505- & [
506- ( "type" , "vnc" ) ,
507- ( "port" , & vnc_port. to_string ( ) ) ,
508- ( "listen" , "127.0.0.1" ) ,
509- ] ,
507+ "model" ,
508+ & [ ( "type" , "virtio" ) , ( "heads" , "1" ) , ( "primary" , "yes" ) ] ,
510509 ) ?;
511- writer. start_element ( "video" , & [ ] ) ?;
512- writer. write_empty_element ( "model" , & [ ( "type" , "vga" ) ] ) ?;
513510 writer. end_element ( "video" ) ?;
511+ writer. start_element ( "channel" , & [ ( "type" , "spicevmc" ) ] ) ?;
512+ writer. write_empty_element (
513+ "target" ,
514+ & [ ( "type" , "virtio" ) , ( "name" , "com.redhat.spice.0" ) ] ,
515+ ) ?;
516+ writer. end_element ( "channel" ) ?;
514517 }
515518
516519 // Virtiofs filesystems
@@ -657,15 +660,28 @@ mod tests {
657660 }
658661
659662 #[ test]
660- fn test_vnc_configuration ( ) {
663+ fn test_graphical_console_configuration ( ) {
664+ // Test with graphical console enabled
661665 let xml = DomainBuilder :: new ( )
662666 . with_name ( "test" )
663- . with_vnc ( 5901 )
667+ . with_graphical_console ( )
668+ . build_xml ( )
669+ . unwrap ( ) ;
670+
671+ assert ! ( xml. contains( "graphics type=\" spice\" autoport=\" yes\" " ) ) ;
672+ assert ! ( xml. contains( "model type=\" virtio\" heads=\" 1\" primary=\" yes\" " ) ) ;
673+ assert ! ( xml. contains( "channel type=\" spicevmc\" " ) ) ;
674+ assert ! ( xml. contains( "target type=\" virtio\" name=\" com.redhat.spice.0\" " ) ) ;
675+
676+ // Test without graphical console (default)
677+ let xml_no_graphics = DomainBuilder :: new ( )
678+ . with_name ( "test-no-graphics" )
664679 . build_xml ( )
665680 . unwrap ( ) ;
666681
667- assert ! ( xml. contains( "graphics type=\" vnc\" port=\" 5901\" " ) ) ;
668- assert ! ( xml. contains( "model type=\" vga\" " ) ) ;
682+ assert ! ( !xml_no_graphics. contains( "<graphics" ) ) ;
683+ assert ! ( !xml_no_graphics. contains( "<video" ) ) ;
684+ assert ! ( !xml_no_graphics. contains( "spicevmc" ) ) ;
669685 }
670686
671687 #[ test]
0 commit comments