`/Sbin/Poweroffvms` В Esx 4.1

Руководство офиса, где я работаю, поставило задачу отработать автоматическое отключение серверов при переходе источника бесперебойного питания на работу от аккумуляторов.

Часть серверов работает на Windows (я о них даже не думал), а часть на esx/esxi, что меня беспокоило больше всего, так как опыта работы с никами у меня очень и очень мало, особенно написания всяких скриптов.

Но задача поставлена и мы должны ее решить.

Я начал потихоньку изучать этот вопрос и был очень рад, когда в esxi 5.x появился бинарник powerOffVms, который отключает гостевые системы при включении соответствующей опции.

Но энтузиазм поубавился, когда в esx-версии такой штуки не нашлось.

В общем, было решено реализовать эту возможность в bash в esx (просто чтобы понять, что он делает и что делает).

Все, что будет ниже, можно реализовать разными способами, что может быть и правильнее, но мне не хотелось отказываться от задуманного.

Первое, что я сделал, это выключил конкретную виртуальную машину.

Для этого необходимо было узнать его состояние, и какие esx-команды для этого использовались.

Возвращаясь к руководствам, у нас есть то, что нам нужно.

vim-cmd vmsvc/power.getstate состояние машины vim-cmd vmsvc/power.shutdown выключение гостевой ОС vim-cmd vmsvc/power.off выключите гостевую ОС В дальнейшем хотелось ясности, а точнее во время выполнения скрипта вывода имени машины, которое можно было бы выдрать из vim-cmd vmsvc/get.summary Зная id виртуальной машины, для наглядности получаем ее имя и пробуем завершить работу гостевой ОС.

Но для того, чтобы корректно завершить работу ОС, нужны установленные инструменты vmware и кнопка «гостевое выключение».

А все дело в том, чтобы проверить, выключился или завис аппарат и как долго проверять процесс выключения.

Ещё раз читаем мануалы и находим такую вещь, как stopDelay, которую можно задать через клиент vsphere, и по умолчанию она составляет 120 секунд. Вы можете взять его отсюда: vim-cmd хостсвк/hostconfig Но и здесь был нюанс: если порядок загрузки не был настроен, то значения этой задержки там не будет. Итак, что мы имеем.

Получение имени машины

  
  
  
  
  
  
  
  
  
  
  
   

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

*"\(.

*\)"[^"]*$/\1/') }

Мы получаем массив значений задержки выключения.

Обратите внимание еще раз, что массив может быть неполным, если не настроен порядок включения.

Дальше будет «костыль» как решить эту проблему.



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

Выключение виртуальной машины.

Функция получает идентификатор машины и значение задержки, через какое время будет отключено питание, если процесс выключения зависнет.

# 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 }

Поскольку в планах была хоть какая-то универсальность скрипта, то отключение конкретной машины оформилось именно в эту функцию.



# 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!!!" }

Здесь я получаю порядок включения виртуальных машин в функцию GetBootOrder, которая возвращает массив значений, индексы которого полностью соответствуют индексам массива из GetStopDelay. И те и другие могут быть не полными (причину я указал выше).

Здесь я использую небольшую хитрость, проще говоря, костыль.

Добавляю в массивы данные по умолчанию для тех машин, которых нет в массиве.

Чтобы понять, какие машины вообще не были настроены, сначала нужно было получить весь их список с помощью: vim-cmd vmsvc/getallvms

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

И добавление в массивы недостающих значений, если они есть.



# 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 }

Ну и сама функция получения порядка загрузки виртуальных машин.



# 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 }

Вернемся снова к выключению виртуальной машины, а точнее к функции GetVmStopDelay, которая определяет, какую задержку мы будем использовать.

Здесь все просто.



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

В результате отключаем конкретную виртуальную машину и проверяем всё целиком; если зависает, то отключается питание (в моей практике такого еще не было).

Далее я поставил себе задачу выключать машины согласно порядку загрузки.

Здесь не нужно было слишком много думать.

Получаем массив со значениями очереди, проходим по каждому и выключаем машину вышеуказанным способом.



# 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" }

Здесь нужно обратить внимание, если машин много, то процесс отключения может занять некоторое время.

А время при переходе на батарею решает все.

Поэтому было решено сделать функцию, которая бы отключала все машины, когда порядок не важен.



# 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 }

В отличие от предыдущей функции, машина выключается без проверки с помощью прямой команды «vim-cmd vmsvc/power.shutdown».

Но что делать, если застряло выключение одной из машин.

Нам нужно проверить результат работы.

Здесь реализована еще одна функция ControlVerboseShutDown, которая будет перепроверять состояние машин через заданный промежуток времени, т.е.

для каждой виртуальной машины по своему stopDelay. Не надо ничего придумывать, все написано - все изучено.



# 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 }

Ну и немного универсальности для вызова самого скрипта с параметрами.



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

Итак мы имеем практически полный аналог powerOffVms esxi 5.1. В процессе написания мы изучили, так сказать, основы bash, утилиты grep и sed и немного регулярных выражений.

Эту проблему, конечно, можно решить и другими способами: Скрипты PowerShell для PowerCLI vMA со сценариями bash пишпере API ВИКСА API Но я на этом не остановился и запустил всё это дело на esxi 5.1, а bash там нет (пришлось добавить).

Обо всем этом можно рассказать в следующей статье, если вам конечно интересно.

Теги: #Виртуализация #bash #esxi #esx #poweroffvms

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.