Руководство офиса, где я работаю, поставило задачу отработать автоматическое отключение серверов при переходе источника бесперебойного питания на работу от аккумуляторов.
Часть серверов работает на 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
-
Создание Ссылок С Помощью Каталогов Мертво?
19 Oct, 24 -
Возможности Web 2.0 И Уязвимости Web 2.0.
19 Oct, 24 -
Ну Вот И Общение!
19 Oct, 24