#!/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;' echo "-k - embed custom public key or create new if 'pub_keyfile' not specified" 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, создаём временный файл из валидных строк if [[ -e "$file" ]]; then touch hosts.tmp echo -n "" > hosts.tmp for line in $(cat "$file"); do ip=$(echo $line | cut -d ' ' -f 1) hostname=$(echo $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 "File hosts.tmp:" cat hosts.tmp; echo ### for line in $(cat hosts.tmp) do ip=$(echo $line | cut -d ' ' -f 1) vlan=$(echo $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 $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