@@ -253,6 +253,10 @@ pub struct LibvirtRunOpts {
253253 #[ clap( long) ]
254254 pub ssh : bool ,
255255
256+ /// Wait for SSH to become available and verify connectivity (for testing)
257+ #[ clap( long) ]
258+ pub ssh_wait : bool ,
259+
256260 /// Mount host container storage (RO) at /run/host-container-storage
257261 #[ clap( long = "bind-storage-ro" ) ]
258262 pub bind_storage_ro : bool ,
@@ -319,6 +323,57 @@ impl LibvirtRunOpts {
319323 }
320324}
321325
326+ /// Wait for SSH to become available on a libvirt domain
327+ ///
328+ /// Polls SSH connectivity by attempting simple commands until successful or timeout.
329+ fn wait_for_ssh_ready (
330+ global_opts : & crate :: libvirt:: LibvirtOptions ,
331+ domain_name : & str ,
332+ timeout_secs : u64 ,
333+ ) -> Result < ( ) > {
334+ use std:: time:: Duration ;
335+
336+ debug ! (
337+ "Waiting for SSH to become available on domain '{}' (timeout: {}s)" ,
338+ domain_name, timeout_secs
339+ ) ;
340+
341+ // Create progress bar
342+ let pb = crate :: boot_progress:: create_boot_progress_bar ( ) ;
343+ pb. set_message ( "Waiting for SSH to become available..." ) ;
344+
345+ // Clone values for closure
346+ let global_opts_clone = global_opts. clone ( ) ;
347+ let domain_name_clone = domain_name. to_string ( ) ;
348+
349+ // Use shared polling function with libvirt-specific test
350+ let ( _elapsed, pb) = crate :: utils:: wait_for_readiness (
351+ pb,
352+ "Waiting for SSH" ,
353+ || {
354+ // Create a test SSH connection with short timeout
355+ let ssh_opts = crate :: libvirt:: ssh:: LibvirtSshOpts {
356+ domain_name : domain_name_clone. clone ( ) ,
357+ user : "root" . to_string ( ) ,
358+ command : vec ! [ "true" . to_string( ) ] , // Simple command to test connectivity
359+ strict_host_keys : false ,
360+ timeout : 5 , // Short timeout for each attempt
361+ log_level : "ERROR" . to_string ( ) ,
362+ extra_options : vec ! [ ] ,
363+ suppress_output : true , // Suppress error messages during connectivity testing
364+ } ;
365+
366+ // Try to connect
367+ crate :: libvirt:: ssh:: run_ssh_impl ( & global_opts_clone, ssh_opts) . map ( |_| true )
368+ } ,
369+ Duration :: from_secs ( timeout_secs) ,
370+ Duration :: from_secs ( 2 ) , // Poll every 2 seconds
371+ ) ?;
372+
373+ pb. finish_and_clear ( ) ;
374+ Ok ( ( ) )
375+ }
376+
322377/// Execute the libvirt run command
323378pub fn run ( global_opts : & crate :: libvirt:: LibvirtOptions , opts : LibvirtRunOpts ) -> Result < ( ) > {
324379 use crate :: images;
@@ -445,12 +500,21 @@ pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtRunOpts) -
445500 }
446501 }
447502
448- if opts. ssh {
503+ if opts. ssh_wait {
504+ // Wait for SSH to be ready and verify connectivity
505+ wait_for_ssh_ready ( global_opts, & vm_name, 180 ) ?;
506+ println ! ( "Ready; use bcvk libvirt ssh to connect" ) ;
507+ Ok ( ( ) )
508+ } else if opts. ssh {
509+ // Wait for SSH then enter interactive shell
510+ wait_for_ssh_ready ( global_opts, & vm_name, 180 ) ?;
511+
449512 // Use the libvirt SSH functionality directly
450513 let ssh_opts = crate :: libvirt:: ssh:: LibvirtSshOpts {
451514 domain_name : vm_name,
452515 user : "root" . to_string ( ) ,
453516 command : vec ! [ ] ,
517+ suppress_output : false ,
454518 strict_host_keys : false ,
455519 timeout : 30 ,
456520 log_level : "ERROR" . to_string ( ) ,
0 commit comments