#!/usr/bin/perl # This example shows how to run a command within a Windows VM and display its # results. The example given below shows the results of stopping the print # spooler service. # # Note that you must be using VIX 1.5 and up on the client side, and your # server must be VMware ESX 3.5 update 1 or higher. use lib qw{ blib/lib blib/auto blib/arch blib/arch/auto/VMware blib/arch/auto }; use strict; use Getopt::Long; use VMware::Vix::Simple; use VMware::Vix::API::Constants; # Find a VM with a given name. The VM must be running. sub FindVM { my ($hostHandle, $name) = @_; my ($vmName); my @vms = FindRunningVMs($hostHandle, 100); my $err = shift @vms; die "Error $err finding running VMs ", GetErrorText($err),"\n" if $err != VIX_OK; foreach (@vms) { $vmName = (split('/', $_))[-1]; $vmName =~ s/\.vmx$//; if ($vmName eq $name) { return $_; } } print "VM $name not found. Please check the name against the list below.\n"; print "Full list of running VMs:\n\t"; print join("\n\t", @vms), "\n"; return undef; } # Run a command in a Windows VM. A valid login and password must also be provided. sub RunCommandInWindowsVM { my ($vmHandle, $user, $password, $command, $waitForCompletion) = @_; my ($err, $results); my ($localCommand, $localOutput, $remoteCommand, $remoteCommandBase, $remoteOutput); # Log in. $err = VMLoginInGuest($vmHandle, $user, $password, 0); # options die "VMLoginInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; # Set up temporary files on locally and on the remote VM. $localCommand = $ENV{TEMP} . "\\command.cmd"; $localOutput = $ENV{TEMP} . "\\output.txt"; ($err, $remoteCommandBase) = VMCreateTempFileInGuest($vmHandle, 0, VIX_INVALID_HANDLE); die "VMCreateTempFileInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; $remoteCommand = $remoteCommandBase . ".cmd"; ($err, $remoteOutput) = VMCreateTempFileInGuest($vmHandle, 0, VIX_INVALID_HANDLE); die "VMCreateTempFileInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; # Put a script on the target host that will redirect output. open(TEMP, ">$localCommand"); print TEMP "$command > $remoteOutput 2>&1\n"; close(TEMP); $err = VMCopyFileFromHostToGuest($vmHandle, $localCommand, $remoteCommand, 0, VIX_INVALID_HANDLE); # Execute the program. VMRunProgramInGuest($vmHandle, $remoteCommand, "", 0, VIX_INVALID_HANDLE); die "VMRunProgramInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; sleep($waitForCompletion); # Get the results. $err = VMCopyFileFromGuestToHost($vmHandle, $remoteOutput, $localOutput, 0, VIX_INVALID_HANDLE); die "VMCopyFileFromGuestToHost failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; # Read the results. open(TEMP, $localOutput); local($/ = undef); $results = (); close(TEMP); # Delete temporary files. unlink($localCommand); unlink($localOutput); $err = VMDeleteFileInGuest($vmHandle, $remoteCommandBase); die "VMDeleteFileInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; $err = VMDeleteFileInGuest($vmHandle, $remoteCommand); die "VMDeleteFileInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; $err = VMDeleteFileInGuest($vmHandle, $remoteOutput); die "VMDeleteFileInGuest failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; # Log out. $err = VMLogoutFromGuest($vmHandle); ReleaseHandle($vmHandle); return $results; } sub usage { print <<'EOM'; Usage: guestcommand.pl --hostname [str] --hostport [int] --hostusername [str] --hostpassword [str] --vmname [str] --vmusername [str] --vmpassword [str] --command [str] Detailed description of options: --hostname: The name or IP of the VMware Workstation, Server or ESX server hosting the VM. --hostport: The port number to use when connecting. --hostusername: The login name to the host that manages this VM. --hostpassword: The password corresponding to hostusername. --vmname: The VM on which to run the command. --vmusername: A login to this VM. The login should have permission to run the command. --vmpassword: The password corresponding to vmusername. --command: The command to run. Note that you will generally need to quote this argument. --connectiontype: The connection type based on the type of server. Leave this unset for ESX. Examples: # Start the print spooler on the Windows VM "mywin" on an ESX 3.5 server # at my.company.com. guestcommand.pl --hostname my.company.com --hostusername root --hostpassword pass --vmname mywin --vmusername Administrator --vmpassword secret --command "net start spooler" # Restart sshd on Redhat-style flavors of Linux (editing PermitRootLogin # is an exercise for you). guestcommand.pl --hostname my.company.com --hostusername root --hostpassword pass --vmname mylin --vmusername root --vmpassword secret --command "service sshd restart" EOM return 1; } my ($err, $hostHandle, $vm, $vmHandle); # Variable defaults, where possible. These are biased toward ESX. my $hostname = ""; my $hostport = 443; my $hostusername = ""; my $hostpassword = ""; my $vmname = ""; my $vmusername = ""; my $vmpassword = ""; my $command = ""; # The VMWARE_VI_SERVER type is used with VMware Server 2.0 and VMware ESX 3.5 and up. my $connectiontype = VIX_SERVICEPROVIDER_VMWARE_VI_SERVER; GetOptions( "hostname:s" => \$hostname, "hostport:i" => \$hostport, "hostusername:s" => \$hostusername, "hostpassword:s" => \$hostpassword, "vmname:s" => \$vmname, "vmusername:s" => \$vmusername, "vmpassword:s" => \$vmpassword, "command:s" => \$command, "connectiontype:i" => \$connectiontype ); # At minimum we need a VM name, user and password, as well as a command to run. if (!$vmname || !$vmusername || !$vmpassword || !$command) { exit usage(); } # Connecting to VI Server requires hostname to be of a certain format. if ($hostname !~ /http/) { $hostname = "https://$hostname/sdk"; } # Connect to the host. #print "$connectiontype ", VIX_SERVICEPROVIDER_VMWARE_VI_SERVER, "\n"; #print "$hostname, $hostport, $hostusername, $hostpassword\n"; ($err, $hostHandle) = HostConnect(VIX_API_VERSION, $connectiontype, $hostname, $hostport, $hostusername, $hostpassword, 0, VIX_INVALID_HANDLE); die "Connect failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; # Find the VM where the command runs. $vm = FindVM($hostHandle, $vmname); if (!$vm) { die "Can't find the VM named $vmname, please check the name and try again.\n"; } # Open the VM. ($err, $vmHandle) = VMOpen($hostHandle, $vm); die "VMOpen failed, $err ", GetErrorText($err), "\n" if $err != VIX_OK; # Run the command and display the output (stdout and stderr) that is generated. my $results = RunCommandInWindowsVM($vmHandle, $vmusername, $vmpassword, $command); print "Results:\n$results"; HostDisconnect($hostHandle);