275 lines
9.0 KiB
Bash
275 lines
9.0 KiB
Bash
#!/bin/bash
|
||
### МЫСЛИ ПО ФИЧАМ
|
||
# Сделать проверку, какие правила НА существуют, и вывести выбор /etc/pve/ha/rules.cfg
|
||
# Сделать замену тега влан
|
||
# Сделать проверку наличия ВМ перед клонированием.
|
||
# Добавить переезд на ноду, выбор ноды. Статистика загрузки нод - в хелп.
|
||
# Добавить тег прокса
|
||
# ИЗУЧИТЬ!!!! /usr/share/pve-docs/examples/guest-example-hookscript.pl
|
||
|
||
storage="syno-tigra"
|
||
#path="/mnt/pve/$storage/snippets"
|
||
path="./snippets"
|
||
node=3
|
||
size=50
|
||
file="hosts"
|
||
pubkey="pub.key"
|
||
|
||
show_help(){
|
||
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 "-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"
|
||
}
|
||
|
||
#Функция создания 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 [[ -n $username ]]; then
|
||
sed -i "s|user: root|user: ${username}\nsudo: ALL=(ALL) NOPASSWD:ALL|" "$snippet"
|
||
fi
|
||
if [[ -n $password ]]; then
|
||
phash=$(cat $password | mkpasswd -m sha-256 -s)
|
||
sed -i "s|.*password.*| - ${phash}|" "$snippet"
|
||
fi
|
||
sed -i "s|.*ssh-rsa.*| - ${pubkey}|" "$snippet"
|
||
echo "Snippet $snippet created"
|
||
}
|
||
|
||
# Обрабатываем опции
|
||
while getopts "a:f:hk:u:p:d: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";;
|
||
t) tag="$OPTARG";;
|
||
*) echo "Invalid option. Use '-h' for help."; exit 1;;
|
||
esac
|
||
done
|
||
# Удаляем обработанные опции, оставляя только аргументы скрипта
|
||
shift "$((OPTIND - 1))"
|
||
echo "DEBUG options amount: $#"
|
||
|
||
### Проверка допустимости опций
|
||
if ! [[ $size -ge 10 && $size -le 500 ]]; then echo "Disk size increment shoud be in range of 10..500. Aborting."; exit 1; fi
|
||
|
||
# Вывод переменных для дебага:
|
||
#if [[ -v file ]]; then echo "File: $file"; fi
|
||
#if [[ -v pubkey ]]; then echo "Public key: $pubkey"; fi
|
||
#if [[ -v username ]]; then echo "User: $file"; fi
|
||
#echo "END DEBUG 1"; exit 101
|
||
|
||
# Если файл не задан, но флаг есть
|
||
if [[ $# -eq 0 && -v "$file" && ! -e "$file" ]]; then
|
||
input=y
|
||
read -p "File $file does not exist. Use default 'hosts' file? Y/n: " input
|
||
case $input in
|
||
y) file=hosts;;
|
||
Y) file=hosts;;
|
||
n) read -p "Enter file name: " file;;
|
||
N) read -p "Enter file name: " file;;
|
||
*) echo "Use '-h' flag for help"; exit 0;;
|
||
esac
|
||
if [[ ! -e "$file" ]]; then
|
||
echo "File $file does not exist. Exiting."
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# Если заданы аргументы И файл
|
||
if [[ $# -ne 0 && -v "$file" ]]; then
|
||
echo "Please enter file OR arguments. Use '-f' flag for help"
|
||
exit 2
|
||
fi
|
||
|
||
# Проверка файла на соответствие шаблону IPv4 адреса и hostname, создаём временный файл из валидных строк
|
||
echo "DEBUG file: $file"
|
||
if [[ $# -eq 0 && -e "$file" ]]; then
|
||
touch hosts.tmp
|
||
echo -n "" > hosts.tmp
|
||
echo "DEBUG"
|
||
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)
|
||
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 3
|
||
fi
|
||
else
|
||
echo "$ip is not a valid IPv4 address (10.10.XXX.YYY). Aborting."
|
||
rm hosts.tmp
|
||
exit 4
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Если заданы аргументы, то создаем временный файл
|
||
if [[ $# -ne 0 ]]; then
|
||
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
|
||
fi
|
||
|
||
# Проверяем ключ
|
||
if [ -v pubkey ]; then
|
||
if [[ ! -f pubkey ]]; then
|
||
input=y
|
||
echo "Public key 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 specify or generate new pair."; exit 6;;
|
||
N) echo "Public key is necessary to continue. Please specify or generate new pair."; exit 6;;
|
||
*) echo "Use '-h' flag for help"; exit 6;;
|
||
esac
|
||
fi
|
||
else
|
||
pubkey="key.pub"
|
||
if [[ ! -e key.pub ]]; then echo "key.pub does not exist. Please use '-k' flag to specify public key."; exit 7; fi
|
||
fi
|
||
|
||
###DEBUG 1
|
||
echo "DEBUG File hosts.tmp:"
|
||
cat hosts.tmp; echo
|
||
###
|
||
|
||
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 "$snippet:"
|
||
cat "$snippet"
|
||
read -p "Press Enter"
|
||
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"
|
||
qm cloudinit update $vmid
|
||
qm start $vmid
|
||
if [ -v 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 [ -v $privkey ]
|
||
then
|
||
echo
|
||
echo "Generated private key: ./$privkey"
|
||
echo "SAVE IT IMMEDIATELY!!!"
|
||
fi |