Files
vmcreate/vmcreate1.sh
2025-10-26 15:53:06 +00:00

275 lines
9.0 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
### МЫСЛИ ПО ФИЧАМ
# Сделать проверку, какие правила НА существуют, и вывести выбор /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