📜 ⬆️ ⬇️

"/ sbin / powerOffVms" in ESX 4.1

The management in the office where I work set the task to work out the automatic shutdown of the servers when the uninterruptible power supply switches to battery operation. Some servers run on windows (I didn’t even think about them) and part on esx / esxi that bothered me the most, because I have very little experience with Nix, especially writing all sorts of scripts. But the task is set and it is necessary to solve it
I began to slowly study this issue, and I was really happy when I entered the binary in esxi 5.x powerOffVms, which quits the guest systems when the corresponding option is turned on. But the enthusiasm diminished when such a thing was not found in the esx version. In general, it was decided to implement this feature on bash in esx (just to understand what it is and what it breathes for).

Everything that goes below now can be implemented in various ways that may be more correct, but we didn’t want to abandon our plans.

The first thing I did was turn off a specific virtual machine. To do this, it was necessary to find out its state, and with what esx commands this is done. Returning to the manual, we have the right ...

vim-cmd vmsvc / power.getstate machine state
vim-cmd vmsvc / power.shutdown guest OS shutdown
vim-cmd vmsvc / power.off power off guest OS
')
In the future, I wanted clarity, or to be more precise, during the execution of the script, the output of the name of the machine that could be torn out of

vim-cmd vmsvc / get.summary

Knowing the id of the virtual machine, we get its name for clarity and try to shut down the guest OS. But in order to properly shut down the OS, you need the installed vmware tools and guest guest shutdown. And the whole thing is to check whether the machine is turned off or hung up and how much time to check the completion process. Once again we smoke manuals and find such a thing as stopDelay, which can be set via the vsphere client, and by default it is 120 seconds. You can tear it out from here:

vim-cmd hostsvc / hostconfig

But even here there was a nuance: if the setting of the boot order was not made, then there would be no value for this delay.
So, what do we have ...

We get the name of the car

# get vm name via VMID # $1 - VMID function GetVMName () { vmName=$(vim-cmd vmsvc/get.summary $1 | grep "name" | sed 's/.*"\(.*\)"[^"]*$/\1/') } 


We get an array of off delay values. Once again I draw attention to the fact that the array may not be complete if the switching order has not been configured. Next will be the "crutch", how to solve this problem.

 # get stop delay options of vms function GetStopDelay () { OUT=$(vim-cmd hostsvc/hostconfig | grep "stopDelay" | sed 's/[^-0-9]//g') stopDelay=( $OUT ) } 


Shut down the virtual machine. The function gets the id of the machine and the value of the delay, how long the power will be turned off if the shutdown process is suspended.

 # vm shutdown # passing parameters to the function # echo "VMShutDown $1 $2" # $1 - VMId, $2 - stopDelay function VMShutDown () { GetVMName $1 stopTime=0 STATE=$(vim-cmd vmsvc/power.getstate $1 | grep "Power") if [ "$STATE" = "Powered off" ] then echo "VM $1 ($vmName) is stopped. " return 1 fi echo "Call VM $1 ($vmName) shutdown..." vim-cmd vmsvc/power.shutdown $1 sleep 5 if [ "$stopTime" -eq 0] then echo "Waiting for VM $1 ($vmName) shutdown..." fi while [ "$STATE" != "Powered off" ] do if [ "$stopTime" -ge "$2" ] then echo "Shutdown of VM $1 ($vmName) causes to fail. Call power off!" vim-cmd vmsvc/power.off $1 return 2 fi STATE=$(vim-cmd vmsvc/power.getstate $1 | grep "Power") stopTime=$(($stopTime+5)) sleep 5 done echo "VM $VM ($vmName) shutdown is successfully" return 3 } 


Since the plans were at least some, but the universality of the script, turning off a particular machine took shape in this function ...

 # specific VM Shutdown # $1 - VMId function SpecificVMShutDown () { GetVMName $1 GetBootOrder element=1 for VM in ${bootOrder[@]} do if [ "$VM" -eq "$1" ] then GetVmStopDelay "${stopDelay[0]}" "${stopDelay["$element"]}" VMShutDown $VM $currentDelay return 1 fi element=$(($element+1)) done echo "VMId $1 is not found!!!" } 


What I'm doing here is getting the order of enabling the virtual machines in the GetBootOrder function, returning an array of values, the indices of which fully correspond to the indices of the array from GetStopDelay. And both of them may not be complete (I indicated the reason above). Then I go to a little trick, in other words a crutch. I add default data to arrays for those machines that are not in the array. To understand which machines were not configured at all, one had to first get their entire list with:

vim-cmd vmsvc / getallvms

 # Get full list of VMs ID function GetAllVMs () { OUT=$(vim-cmd vmsvc/getallvms |grep -o '^[0-9]*') allVMs=( $OUT ) } 


And adding missing values ​​to arrays, if any ...

 # Find missed VMs in boot order # Add missed VM to boot order array function FindMissedVMs () { GetAllVMs for aVM in ${allVMs[@]} do exists=0 for oVM in ${bootOrder[@]} do if [ "$aVM" -eq "$oVM" ] then exists=1 break fi done if [ "$exists" -eq 0 ] then bootOrder=( "${bootOrder[@]}" "$aVM" ) stopDelay=( "${stopDelay[@]}" "-1" ) fi done } 


Well, the very function of obtaining the order of loading virtual machines ...

 # get boot order of vms function GetBootOrder () { OUT=$(vim-cmd hostsvc/hostconfig | grep "key = 'vim.VirtualMachine:" | sed 's/[^-0-9]//g') bootOrder=( $OUT ) GetStopDelay FindMissedVMs } 


Again, back to turning off the virtual machine, or rather to the function GetVmStopDelay, which determines which delay we will use. It's simple.

 # use default or optional delay # $1 - default delay, $2 optional delay function GetVmStopDelay () { currentDelay="$1" if [ "$2" -gt 0 ] then currentDelay="$2" fi } 


At the output, we get a shutdown of a specific virtual machine and a check of the whole case; in the case of a hangup, the power is turned off (in my practice this has not happened yet).

Then I set myself the task to turn off the machines according to the boot order. There was no need to think much. We get an array with the values ​​of the queue, run through each one and turn off the machine in the above way.

 # order shutdown all VMS function OrderShutDown () { echo "Call order shutdown all VMs" GetBootOrder element=1 for VM in ${bootOrder[@]} do #echo "${stopDelay["$element"]}" GetVMName $VM echo "Beginning shutdown process: $vmName (VMID: $VM)..." GetVmStopDelay "${stopDelay[0]}" "${stopDelay["$element"]}" VMShutDown $VM $currentDelay element=$(($element+1)) done echo "Order shutdown has been executed" } 


Here it is necessary to pay attention, if there are a lot of machines, then the shutdown process may take some time. And the time when switching to a battery is our everything. Therefore, it was decided to make a function that will cause all the machines to shut down when order is not important.

 # verbose shutdown all VMS function VerboseShutDown () { echo "Call verbose shutdown all VMs" GetBootOrder for VM in ${bootOrder[@]} do STATE=$(vim-cmd vmsvc/power.getstate $VM | grep "Power") if [ "$STATE" != "Powered off" ] then GetVMName $VM echo "Call VM $VM ($vmName) shutdown" vim-cmd vmsvc/power.shutdown $VM fi done ControlVerboseShutDown } 


Unlike the previous function, the machine is turned off without checking, with the direct command “vim-cmd vmsvc / power.shutdown”. But what if the completion of the work of some of the machines stuck. It is necessary to check the result of the work ... One more function ControlVerboseShutDown is implemented here, which will recheck the state of the machines after a specified period of time, i.e. for each virtual machine according to its stopDelay. It is not necessary to invent anything, everything is written - everything has been studied.

 # control process off verbose shutdown function ControlVerboseShutDown () { echo "Checking verbose shutdown all VMs" executed=0 stopTime=0 while [ "$executed" -eq 0 ] do errorCount=0 element=1 for VM in ${bootOrder[@]} do GetVMName $VM STATE=$(vim-cmd vmsvc/power.getstate $VM | grep "Power") if [ "$STATE" = "Powered off" ] then #echo "VM $VM ($vmName) is powered off. Checking next" element=$(($element+1)) continue fi if [ "$stopTime" -eq 0] then echo "Waiting for VM $1 ($vmName) shutdown..." fi GetVmStopDelay "${stopDelay[0]}" "${stopDelay["$element"]}" if [ "$stopTime" -ge "$currentDelay" ] then echo "Shutdown of VM $VM ($vmName) causes to fail. Call power Off!" vim-cmd vmsvc/power.off $VM fi errorCount=$(($errorCount+1)) element=$(($element+1)) done if [ "$errorCount" -eq 0 ] then echo "Verbose shutdown has been executed" executed=1 return 1 fi stopTime=$(($stopTime+10)) sleep 10 echo "Remaining time: $stopTime" done } 


Well, a little versatility to call the script itself with the parameters ...

 while getopts ":os:v" optname do case "$optname" in "o") OrderShutDown exit 1 ;; "s") VMID=$OPTARG SpecificVMShutDown "$VMID" exit 2 ;; "v") VerboseShutDown exit 3 ;; esac done 


That is almost the complete analogue of powerOffVms esxi 5.1. In the process of writing, the basics of bash, the grep and sed utilities, and a few regular expressions were studied, so to speak.
This problem, of course, can be solved in other ways:

PowerShell scripts for PowerCLI
vMA with bash scripting
pyshpere api
VIX API

But I didn’t stop there and started the whole thing on esxi 5.1, but there is no bash there (I had to add it). All this can be described in the next article, if you are, of course, interested.

Source: https://habr.com/ru/post/188726/


All Articles