Relevant specs:
- Mac OS 10.8.3
- Fusion 4.1.4
Virtual machines:
- Windows XP Pro imported from Boot Camp
- Windows 7 Boot Camp
VMware recommends that virtual machines (VMs) not be backed up using Apple's TimeMachine, unless snapshots are used. However, many advanced, experienced users strongly discourage that snapshots be used, as each snapshot chains on the prior one, meaning that the failure of any link can cause your VM to fail, as well as increasing the complexity of your VM and reducing its performance.
So, I'm trying to write an AppleScript that powers down any VMs running, then copies a backup. That should be clean and simple.
However, I've run into some problems with a suspended VM (so Boot Camp VMs aren't a problem since they can't suspend). If a VM is suspended, then you need it to be powered off (shut down) in order to back it up cleanly. The only way to power off a suspended VM is to first resume it, then power it off. That's not a problem for AppleScript if the VM is open in its own window. VMWare Fusion correctly reports this VM as suspended, allowing the script to first resume it, then shut it down.
Unfortunately, if the VM window has been closed so that it doesn't have its own window, but rather only appears in the Virtual Machine Library, then VMware Fusion does not correctly report the VM as suspended. Even if the VM is suspended, VMware Fusion reports that state as "powered off", meaning that a suspended VM would be backed up.
I've tried to "resume" all of the VMs, but this actually starts any VMs that are "powered off" (shut down), which, obviously is a disaster (how long do I wait for multiple VMs to start up simultaneously before it's safe to force them to shut down?).
I've tried to open the VM's window, but if the VM windows are closed, then AppleScript only sees two windows: "Virtual Machine Library" and "Updates". The AppleScript "Virtual Machine Library" window object doesn't appear to have any ability to access the individual VM windows (even though I can right-click on a VM in the "Virtual Machine Library" and choose "Show Windows" in order to open up the VM window and get it into the state that I need. Also, the AppleScript window is able to reference / access the VM "document" (or AppleScript object / VM), but the VM "document" (which I'm able to access) isn't able to reference / access its own window. (So, the window contains the document, allowing the window to access the document, but the document inside the window has no ability to reference the window which contains it.)
At this point, I'm looking at setting VMWare Fusion "Preferences…" -> "General" -> "When closing a virtual machine:" to "Power off the virtual machine". As long as users don't specifically choose to suspend a VM, then their VMs are automatically shut down ("powered off"). Meaning, I'm left with crossing my fingers that no users change this setting, nor suspend their VMs.
Does any one have any ideas?
Am I having this problem because the specific VM that I'm testing is imported? (Would a normally created VM not incorrectly report its state when it doesn't have a window?)
Is this fixed in VMware Fusion 5? (We would probably upgrade if that's the case.)
Also, if anyone reports this bug to VMware, then please note that in a response below. Otherwise, I'll report it (assuming that no one shows to me that it isn't actually a bug).
And, for anyone who'd like to help with the code, what I have so far is found below with some test values commenting out production values. You'll want to save it as an application, then run it like any normal application.
Thanks, in advance!
<code>
(*
Applescript application to auto back up virtual machines after certain period of system idle
Requirements:
[ ] "Power off the virtual machine" must be selected in VMWare Fusion "Preferences…" -> "General" -> "When closing a virtual machine:" -- this is needed because if a VM window is closed, then VMware reports the power state of a VM as "powered off" even if the VM is actually "suspended"; and from a document object there is no way to access its window
http://culturedcode.com/forums/read.php?7,30174,30597
To make this runable, save as application.
To not show in Dock, set LSBackgroundOnly in Info.plist of created app bundle, or other ways in
http://groups.google.com/group/macenterprise/browse_thread/thread/be7db35451e1dc70
*)
property afterHours : 8 --21 -- hour of the day to start monitoring idle
property workDayStarts : 5 -- hour of the day to stop monitoring idle
property backupDuration : 2 -- number of hours needed to back up virtual machines
global backup_after, check_every -- TODO: do these really need to be global ?
set backup_after to 10 --(40 * 60) -- in seconds
set check_every to 5 --(10 * 60)
property cancelTimer : 60 -- how many seconds the user has to cancel backup
property virtualizationApp : "VMware Fusion" -- variable can't be used in 'tell application' statements or won't compile, so search & replace other instances of "VMware Fusion"
property resumeDelay : 40 --(4 * 60) -- in seconds
onreopen
display dialog "After " & (backup_after / 60) & " minutes of system inactivity this AppleScript powers down any running virtual machines and backs them up. A check for inactivity is performed every " & (check_every / 60) & " minutes."
endreopen
onidle
tellapplication "System Events"
-- verify that are in allowed timeframe for backups
set currentHour to (time of (current date)) div hours
if currentHour isless than (workDayStarts - backupDuration) or currentHour isgreater than afterHours then
-- check that user has been idle / inactive for sufficient time
set idletime todo shell script "echo $((`ioreg -c IOHIDSystem | sed -e '/HIDIdleTime/ !{ d' -e 't' -e '}' -e 's/.* = //g' -e 'q'` / 1000000000))"
if (idletime asinteger) > backup_after then
--TODO: test to see if backup has been done today
-- warn the user that VM application will force close VMs
repeatwith secondsLeft from cancelTimer to 1 by -1
set dialogResult todisplay alert virtualizationApp & " is about to force quit and back up virtual machines " message "This AppleScript is set to force close " & virtualizationApp & ", if it's open, and its virtual machines, and then to back up the virtual machines." & return & return & "Click \"Cancel\" to delay these actions until later, or press \"OK\" to quit and back up." & return & return & secondsLeft & " seconds left." buttons {"OK", "Cancel"} default button "OK" as warning giving up after 1
if secondsLeft = 1 or (button returned of dialogResult) is "OK" then
-- force VMs to power off, quit VM app
try
tellapplication "VMware Fusion"
activate -- can't test to see if a VM is suspended unless VMware is running
delay 10 -- give the application time to open
repeatwith aObj indocuments
set powerState to power state of aObj
if powerState is powered on then
power off aObj with force -- doesn't work on suspended VMs
elseif powerState is suspended then -- VM reports powered off even when actually suspended, works if VM's own window open (VM Library, right-click on VM, then select "Show Windows"
resume aObj -- only want to resume if actually suspended, otherwise it starts powered off VMs
delay resumeDelay -- give VM time to resume; usually takes < 30s, but if multiple VM running, then can be ~2min
power off aObj with force
endif
endrepeat
quit
endtell
onerror errMsg number errNum
display dialog ("errMsg: " & errMsg & ", errNum: " & errNum)
endtry
-- back up VMs
exitrepeat
elseif (button returned of dialogResult) is "Cancel" then
display dialog "Backup has been delayed until later."
exitrepeat
endif
endrepeat
endif
return idletime
endif
endtell
endidle
</code>