Files
vmcreate/vmcreate.sh
2025-10-28 12:29:58 +00:00

327 lines
11 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
### ВКЛЮЧИТЬ qm start !!!!!!!!!!!
### МЫСЛИ ПО ФИЧАМ
# Сделать проверку, какие правила НА существуют, и вывести выбор /etc/pve/ha/rules.cfg
# Добавить переезд на ноду, выбор ноды. Статистика загрузки нод - в хелп.
# ИЗУЧИТЬ!!!! /usr/share/pve-docs/examples/guest-example-hookscript.pl
storage="syno-tigra"
#path="/mnt/pve/$storage/snippets"
path="./snippets/" #SEE BELOW!!!
mkdir $path #REMOVE AFTER CORRECT PATH!!!!!!!!
node=3
size=50
# Далее объявляю переменные, в цикле getopts это не получается
harule=""
pubkey=""
username=""
password=""
tag=""
file=""
# Конец костыля
show_help () {
echo
echo 'Usage:'
echo 'vmcreate [-h][-a rule_name][-k pub_keyfile][-u][-p][-d 50][-t][-f filename]'
echo 'Arguments:'
echo '-h - show this help.'
echo '-a - add hosts to HA affinity rules with name 'rule_name'.'
echo "-k - embed custom public key or create new if 'pub_keyfile' does not exist."
echo "-u - specify user instead of default 'root'."
echo "-p - specify password instead of default."
echo "-d - add custom disk space (in gibibytes, integer). Default is 50."
echo "-n - specify target node NUMBER to migrate VM to after creating. Default is '3'"
echo "-t - add additional proxmox tag. Default is only pve node number."
echo "-f - get IP addresses and Hostnames from 'flilename'."
echo
echo "If file not specified, script will use arguments as a list of IP addresses."
echo "In this case Hostname will be inherited from 2 last IP octets. Example for 10.10.35.20: 'vm035020'."
echo "IP address should be 10.10.XXX.YYY"
echo
}
#Функция создания VMID, аргумент - IPv4 адрес.
get_vmid () {
local vlan=$(echo -n $1 | cut -d '.' -f 3)
local oct4=$(echo -n $1 | cut -d '.' -f 4)
local vmid=""
if [[ $vlan -eq 0 ]]; then
vmid=100
else
vmid=$(printf "%02d\n" "$vlan")
fi
vmid+=$(printf "%03d\n" "$oct4")
echo -n $vmid
}
# Функция для проверки IP адреса по шаблону 10.10.*.*, написана гуглом
ipcheck () {
local ip=$1
local stat=1
#oldIFS=$IFS
# Check if the IP matches the general IPv4 pattern
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
# Split the IP into octets
IFS='.' read -r -a octets <<< "$ip"
# Check if each octet is within the valid range (0-255)
if [[ ${octets[0]} -eq 10 && ${octets[1]} -eq 10 && ${octets[2]} -le 252 && ${octets[3]} -le 252 && ${octets[3]} -ne 0 ]]; then
stat=0
fi
fi
return $stat
#IFS=$oldIFS
}
# Функция для проверки hostname на валидные символы (буквы, цифры, дефис)
hostcheck () {
local hostname=$1
local pattern="^[a-zA-Z0-9-]+$"
if [[ "$hostname" =~ $pattern ]]; then
return 0
else
return 1
fi
}
keypair_generate () {
ssh-keygen -t rsa -N "" -f $privkey
pubkey="$privkey".pub
echo "Keypair generated."
}
mksnippet () {
snippet="${path}${vmid}_user.yaml"
cp ./user.yaml "$snippet"
sed -i "s/HOSTNAME/$hostname/g" "$snippet"
if [ $username ]; then
sed -i "s|user: root|user: ${username}\nsudo: ALL=(ALL) NOPASSWD:ALL|" "$snippet"
fi
if [ $password ]; then
phash=$(cat $password | mkpasswd -m sha-256 -s)
sed -i "s|.*password.*|password: ${phash}|" "$snippet"
fi
if [ $pubkey ]; then
sed -i "s|.*ssh-rsa.*| - $(cat ${pubkey})|" "$snippet"
fi
echo "Snippet $snippet created"
}
# Обрабатываем опции
while getopts "a:f:hk:u:p:d:n:t:" opt; do
case $opt in
a) harule=${OPTARG};;
f) file=${OPTARG};;
h) show_help; exit 0;;
k) pubkey=${OPTARG};;
u) username=${OPTARG};;
p) password=${OPTARG};;
d) size=${OPTARG};;
n) node=${OPTARG};;
t) tag=${OPTARG};;
\?) echo "Invalid option."; show_help; exit 1;;
esac
done
if [ $harule ]; then
hostcheck "$harule"
if [ $? -ne 0 ]; then echo "HArule should contain only letters, numbers and dashes. Aborting."; exit 1; fi
fi
if [[ $node -lt 1 || $node -gt 4 ]]; then
echo "Node number is not in [1..4]. Please specify correct node number. Aborting"
fi
# DEBUG Print specified options
if [ $harule ]; then echo "harule: $harule"; fi
if [ $file ]; then echo "file: $file"; fi
if [ $pubkey ]; then echo "pubkey: $pubkey"; fi
if [ $username ]; then echo "username: $username"; fi
if [ $password ]; then echo "password: $password"; fi
if [ $size ]; then echo "size: $size"; fi
if [ $tag ]; then echo "tag: $tag"; fi
# END DEBUG
# Удаляем обработанные опции, оставляя только аргументы скрипта
shift "$((OPTIND - 1))"
echo "DEBUG arguments amount: $#"
### Проверка допустимости опций
if ! [[ $size -ge 10 && $size -le 500 ]]; then echo "Disk size increment shoud be in range of 10..500. Aborting."; exit 2; fi
# Создаём hosts.tmp из аргументов/файла, попутно проверяя данные
# Если заданы И аргументы, И файл - сразу нахуй
if [[ $# -ne 0 && $use_file ]]; then
echo "Please use EITHER file OR arguments. Use '-h' flag for help"
exit 3
else
# Если файл задан
if [ $file ]; then
# Но не существует, то нахуй
if [ ! -f "$file" ];then
echo "File $file does not exist. Aborting."
exit 4
fi
fi
# Если заданы аргументы
if [ $# -ne 0 ]; then
echo "DEBUG using arguments"
touch hosts.tmp
echo -n "" > hosts.tmp
for arg in "$@"; do
ipcheck "$arg"
if [ $? -eq 0 ]; then
echo -n "$arg;" >> ./hosts.tmp
echo -n "vm" >> ./hosts.tmp
get_vmid "$arg" >> hosts.tmp
echo >> hosts.tmp
else
echo "Argument $arg is not a valid IPv4 address (10.10.*.*). Aborting."
rm hosts.tmp
exit 5
fi
done
# А если нет, то файл.
else
echo "DEBUG using file: $file"
touch hosts.tmp
echo -n "" > hosts.tmp
echo "DEBUG cat file"
cat "$file"
for line in $(cat "$file"); do
echo "DEBUG line:$line"
ip=$(echo -n $line | cut -d ';' -f 1)
hostname=$(echo -n $line | cut -d ';' -f 2)
ipcheck "$ip"
if [[ $? -eq 0 ]]; then
hostcheck "$hostname"
if [[ $? -eq 0 ]]; then
echo $line >> hosts.tmp
else
echo "Hostname $hostname is not valid. Should contain only letters, numbers and dash. Aborting."
rm hosts.tmp
exit 5
fi
else
echo "$ip is not a valid IPv4 address (10.10.XXX.YYY). Aborting."
rm hosts.tmp
exit 5
fi
done
fi
fi
# Закончили с вводными данными
# Проверяем ключ
if [ $pubkey ]; then
if [[ ! -f "$pubkey" ]]; then
input=y
echo "You specified public key but it does not exist. Generate new pair? Y/n"
read input
case $input in
y) read -p "Enter name for your PRIVATE key: " privkey && keypair_generate "$privkey";;
Y) read -p "Enter name for your PRIVATE key: " privkey && keypair_generate "$privkey";;
n) echo "Public key is necessary to continue. Please use default, specify or generate new pair."; exit 6;;
N) echo "Public key is necessary to continue. Please use default, specify or generate new pair."; exit 6;;
*) echo "Use '-h' flag for help"; exit 6;;
esac
fi
else
read -p "Public key not specified. Use default? Enter to continue or Ctrl+C to abort"
fi
# Проверяем, что нет ВМ в списке и нет сниппетов
for line in $(cat hosts.tmp)
do
ip=$(echo -n "$line" | cut -d ';' -f 1)
vmid=$(get_vmid "$ip")
snippet=
if qm status "$vmid" &>/dev/null; then
echo "VM $vmid exists. Aborting"
exit 7
elif [[ -f "$snippet" ]]; then
echo "Snippet $snippet exists. Aborting"
exit 7
fi
done
### DEBUG 1
echo "DEBUG cat hosts.tmp:"
cat hosts.tmp; echo
###
# Подготовительные операции закончены, приступаем к выполнению.
read -p "Script is ready to create $(cat hosts.tmp | wc -l) VMs. Press Enter"
for line in $(cat hosts.tmp)
do
ip=$(echo -n $line | cut -d ';' -f 1)
vlan=$(echo -n $ip | cut -d '.' -f 3)
if [[ $vlan -eq 0 ]]; then
mask=23
gw="10.10.0.1"
vlan=100
else
mask=24
gw="10.10.${vlan}.1"
fi
hostname=$(echo -n $line | cut -d ';' -f 2)
vmid=$(get_vmid "$ip")
mksnippet
#### DEBUG 2
echo "IP: $ip"
echo "hostname: $hostname"
echo "VMID: $vmid"
echo "VLAN: $vlan"
echo "TAG: $tag"
echo "$snippet:"
cat "$snippet"
exit 100
####
echo -n "Now cloning VM $vmnum from a template....."
qm clone 5000 $vmid --name $hostname --full > /dev/null
if [ $? -eq 0 ]; then
echo "OK"
else
echo "ERROR"
exit 8
fi
qm set $vmid --tags "${node}${tag}"
qm resize $vmid scsi0 +"$size"G
qm set $vmid --cicustom "user=${storage}:snippets/${vmnum}_user.yaml"
qm set $vmid --ipconfig0 ip="$ip"/"$mask",gw="$gw"
sed -i "s|tag=35|tag=$vlan|" /etc/pve/qemu-server/${vmid}.conf
qm cloudinit update $vmid
### DEBUG
echo "DEBUG: ${vmid}.conf"
cat /etc/pve/qemu-server/${vmid}.conf
exit 102
###
# # Migrate VM to target node
# echo -n "Migrating VM $vmid to pve${node}....."
# qm migrate $vmid pve${node} &&
# if [ $? -eq 0 ]; then
# echo "OK"
# else
# echo "ERROR"
# exit 9
# fi
# qm start $vmid ВРЕМЕННО ОТКЛЮЧЕНО
if [ $harule ]; then
ha-manager add vm:$vmid --state started --max_relocate 2
ha-manager rules add node-affinity $harule --resources vm:$vmid --nodes pve1,pve2,pve3 --strict 1
echo "HA rule added"
# ОБЯЗАТЕЛЬНО ПРОВЕРИТЬ КОМАНДУ!!!
fi
done
# Финальная часть
rm hosts.tmp
if [ $privkey ]; then
echo
echo "Generated private key: ./$privkey"
echo "SAVE IT IMMEDIATELY!!!"
fi