@@ -51,6 +51,7 @@ const TASKS: &[(&str, fn(&Shell, &[String]) -> Result<()>)] = &[
5151 ( "package-srpm" , package_srpm) ,
5252 ( "spec" , spec) ,
5353 ( "run-tmt" , run_tmt) ,
54+ ( "tmt-provision" , tmt_provision) ,
5455] ;
5556
5657fn try_main ( ) -> Result < ( ) > {
@@ -410,6 +411,7 @@ fn verify_ssh_connectivity(sh: &Shell, port: u16, key_path: &Utf8Path) -> Result
410411 sh,
411412 "ssh -i {key_path} -p {port_str} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -o IdentitiesOnly=yes root@localhost 'export TEST=value; whoami'"
412413 )
414+ . ignore_stderr ( )
413415 . read ( ) ;
414416
415417 match & result {
@@ -704,6 +706,94 @@ fn run_tmt(sh: &Shell, args: &[String]) -> Result<()> {
704706 Ok ( ( ) )
705707}
706708
709+ /// Provision a VM for manual tmt testing
710+ /// Wraps bcvk libvirt run and waits for SSH connectivity
711+ ///
712+ /// Arguments:
713+ /// - First arg (required): Image name (e.g. "localhost/bootc-integration")
714+ /// - Second arg (optional): VM name (defaults to "bootc-tmt-manual-<timestamp>")
715+ ///
716+ /// Prints SSH connection details for use with tmt provision --how connect
717+ #[ context( "Provisioning VM for TMT" ) ]
718+ fn tmt_provision ( sh : & Shell , args : & [ String ] ) -> Result < ( ) > {
719+ // Check for bcvk
720+ if cmd ! ( sh, "which bcvk" ) . ignore_status ( ) . read ( ) . is_err ( ) {
721+ anyhow:: bail!( "bcvk is not available in PATH" ) ;
722+ }
723+
724+ // Parse arguments
725+ if args. is_empty ( ) {
726+ anyhow:: bail!( "Image name is required as first argument" ) ;
727+ }
728+
729+ let image = & args[ 0 ] ;
730+ let vm_name = if args. len ( ) > 1 {
731+ args[ 1 ] . clone ( )
732+ } else {
733+ let timestamp = std:: time:: SystemTime :: now ( )
734+ . duration_since ( std:: time:: UNIX_EPOCH )
735+ . context ( "Getting timestamp" ) ?
736+ . as_secs ( ) ;
737+ format ! ( "bootc-tmt-manual-{}" , timestamp)
738+ } ;
739+
740+ println ! ( "Provisioning VM..." ) ;
741+ println ! ( " Image: {}" , image) ;
742+ println ! ( " VM name: {}\n " , vm_name) ;
743+
744+ // Launch VM with bcvk
745+ // Use ds=iid-datasource-none to disable cloud-init for faster boot
746+ cmd ! ( sh, "bcvk libvirt run --name {vm_name} --detach --filesystem ext4 --karg=ds=iid-datasource-none {image}" )
747+ . run ( )
748+ . context ( "Launching VM with bcvk" ) ?;
749+
750+ println ! ( "VM launched, waiting for SSH..." ) ;
751+
752+ // Wait for VM to be ready and get SSH info
753+ let ( ssh_port, ssh_key) = wait_for_vm_ready ( sh, & vm_name) ?;
754+
755+ // Save SSH private key to target directory
756+ let key_dir = Utf8Path :: new ( "target" ) ;
757+ sh. create_dir ( key_dir)
758+ . context ( "Creating target directory" ) ?;
759+ let key_path = key_dir. join ( format ! ( "{}.ssh-key" , vm_name) ) ;
760+
761+ std:: fs:: write ( & key_path, ssh_key)
762+ . context ( "Writing SSH key file" ) ?;
763+
764+ // Set proper permissions on key file (0600)
765+ #[ cfg( unix) ]
766+ {
767+ use std:: os:: unix:: fs:: PermissionsExt ;
768+ std:: fs:: set_permissions ( & key_path, std:: fs:: Permissions :: from_mode ( 0o600 ) )
769+ . context ( "Setting SSH key file permissions" ) ?;
770+ }
771+
772+ println ! ( "SSH key saved to: {}" , key_path) ;
773+
774+ // Verify SSH connectivity
775+ verify_ssh_connectivity ( sh, ssh_port, & key_path) ?;
776+
777+ println ! ( "\n ========================================" ) ;
778+ println ! ( "VM provisioned successfully!" ) ;
779+ println ! ( "========================================" ) ;
780+ println ! ( "VM name: {}" , vm_name) ;
781+ println ! ( "SSH port: {}" , ssh_port) ;
782+ println ! ( "SSH key: {}" , key_path) ;
783+ println ! ( "\n To use with tmt:" ) ;
784+ println ! ( " tmt run --all provision --how connect \\ " ) ;
785+ println ! ( " --guest localhost --port {} \\ " , ssh_port) ;
786+ println ! ( " --user root --key {} \\ " , key_path) ;
787+ println ! ( " plan --name <PLAN_NAME>" ) ;
788+ println ! ( "\n To connect via SSH:" ) ;
789+ println ! ( " ssh -i {} -p {} -o IdentitiesOnly=yes root@localhost" , key_path, ssh_port) ;
790+ println ! ( "\n To cleanup:" ) ;
791+ println ! ( " bcvk libvirt rm --stop --force {}" , vm_name) ;
792+ println ! ( "========================================\n " ) ;
793+
794+ Ok ( ( ) )
795+ }
796+
707797fn print_help ( _sh : & Shell , _args : & [ String ] ) -> Result < ( ) > {
708798 println ! ( "Tasks:" ) ;
709799 for ( name, _) in TASKS {
0 commit comments