diff options
| author | haocheng <email@a.nub.ninja> | 2024-09-10 17:11:31 +0800 |
|---|---|---|
| committer | haocheng <email@a.nub.ninja> | 2024-09-10 17:11:31 +0800 |
| commit | 00cde1e081c2a31ddc5876d52342888c0926af7a (patch) | |
| tree | 7a10d40b438549f3a920837093d55da0ba17c0fa /vm.sh | |
hiiii
Diffstat (limited to 'vm.sh')
| -rw-r--r-- | vm.sh | 316 |
1 files changed, 316 insertions, 0 deletions
| @@ -0,0 +1,316 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | help(){ | ||
| 4 | echo -e "\n$0 | ||
| 5 | info | ||
| 6 | create\n -vcpu\n -ram \n -disk \n -ssh-keys-dir \n -add-disk-block\n -public-ssh-port | ||
| 7 | manage\n -start\n -shutdown\n -reboot\n -kill\n -erase | ||
| 8 | disk\n -attach\n -detach\n -resize\n" | ||
| 9 | exit 1 | ||
| 10 | } | ||
| 11 | |||
| 12 | case "$1" in | ||
| 13 | "create") | ||
| 14 | if [[ $# -lt 2 ]]; then | ||
| 15 | echo "" | ||
| 16 | echo -e "$0 $1 [VM_NAME]" | ||
| 17 | echo -e "default: -vcpu 8 -ram 8 -disk 60 --ssh-keys-dir keys/" | ||
| 18 | echo -e "args[GB]:\n -vcpu\n -ram \n -disk \n -ssh-keys-dir \n -add-disk-block\n -public-ssh-port)" | ||
| 19 | echo "" | ||
| 20 | exit 1 | ||
| 21 | fi | ||
| 22 | |||
| 23 | VM_NAME=$2 | ||
| 24 | VCPU=8 | ||
| 25 | RAM_GB=8000 #memory is noted as mb in the virt-install program | ||
| 26 | DISK_GB=64 | ||
| 27 | SSH_KEYS_DIR="keys/" | ||
| 28 | DISK_BLOCK_GB=0 | ||
| 29 | PSP=${PSP:-0} | ||
| 30 | |||
| 31 | while [[ $# -gt 0 ]]; do | ||
| 32 | case "$1" in | ||
| 33 | create) | ||
| 34 | shift 2 | ||
| 35 | ;; | ||
| 36 | -vcpu) | ||
| 37 | VCPU=$2 | ||
| 38 | shift 2 | ||
| 39 | ;; | ||
| 40 | -ram) | ||
| 41 | RAM_GB=$(($2*1000)) | ||
| 42 | shift 2 | ||
| 43 | ;; | ||
| 44 | -disk) | ||
| 45 | DISK_GB=$2 | ||
| 46 | shift 2 | ||
| 47 | ;; | ||
| 48 | -ssh-keys-dir) | ||
| 49 | SSH_KEYS_DIR=$2 | ||
| 50 | shift 2 | ||
| 51 | ;; | ||
| 52 | -add-disk-block) | ||
| 53 | DISK_BLOCK_GB=$2 | ||
| 54 | shift 2 | ||
| 55 | ;; | ||
| 56 | -public-ssh-port) | ||
| 57 | PSP=$2 | ||
| 58 | shift 2 | ||
| 59 | ;; | ||
| 60 | *) | ||
| 61 | echo "Unknown argument: $1" | ||
| 62 | exit 1 | ||
| 63 | ;; | ||
| 64 | esac | ||
| 65 | done | ||
| 66 | download_url="https://download.rockylinux.org/pub/rocky/9.3/images/x86_64/Rocky-9-GenericCloud-LVM-9.3-20231113.0.x86_64.qcow2" | ||
| 67 | dir_path="/var/lib/libvirt/images/" | ||
| 68 | src_file="/var/lib/libvirt/images/Rocky9.3.qcow2" | ||
| 69 | seed_iso="/var/lib/libvirt/images/seed.iso" | ||
| 70 | packages=("nc" "htop" "wireguard-tools" "bind-utils" "tmux" "net-tools" "curl" "mlocate" "dnsmasq" "qemu-kvm" "libvirt" "libvirt-client" "bridge-utils" "virt-install" "virt-manager" "genisoimage") | ||
| 71 | NEW_IMG_PATH="/var/lib/libvirt/images/${VM_NAME}.qcow2" | ||
| 72 | XML_PATH="/tmp/${VM_NAME}.xml" | ||
| 73 | DISK_BLOCK_GB_BASE_PATH="/var/lib/libvirt/images/${VM_NAME}-vm-disks/" | ||
| 74 | DISK_BLOCK_GB_PATH="${DISK_BLOCK_GB_BASE_PATH}${VM_NAME}-vda-${DISK_BLOCK_GB}G.qcow2" | ||
| 75 | ssh_private=$(cat /home/s22/man1) | ||
| 76 | |||
| 77 | function check_package_installed() { | ||
| 78 | local package_name=$1 | ||
| 79 | rpm -q "$package_name" &> /dev/null | ||
| 80 | } | ||
| 81 | |||
| 82 | for package in "${packages[@]}"; do | ||
| 83 | if ! check_package_installed "$package"; then | ||
| 84 | #echo "Downloading packages..." | ||
| 85 | sudo dnf install -y "$package" &> /dev/null | ||
| 86 | fi | ||
| 87 | done | ||
| 88 | |||
| 89 | [ ! -d $dir_path ] && mkdir -p $dir_path &> /dev/null | ||
| 90 | [ ! -f "$src_file" ] && (wget -O "$src_file" "$download_url" &> /dev/null || { echo "Failed to download Rocky"; exit 1; }) | ||
| 91 | |||
| 92 | echo -e "#cloud-config\nusers:\n - name: root\n ssh-authorized-keys:" > user-data | ||
| 93 | |||
| 94 | for key in "$SSH_KEYS_DIR"/*.pub; do | ||
| 95 | echo " - $(cat "$key")" >> user-data | ||
| 96 | done | ||
| 97 | |||
| 98 | echo " sudo: ['ALL=(ALL) NOPASSWD:ALL'] | ||
| 99 | groups: sudo | ||
| 100 | shell: /bin/bash" >> user-data | ||
| 101 | |||
| 102 | if [ "$PSP" -ne 0 ]; then | ||
| 103 | echo "write_files:" >> user-data | ||
| 104 | echo " - path: /root/man1" >> user-data | ||
| 105 | echo " content: |" >> user-data | ||
| 106 | while read -r line; do | ||
| 107 | echo " $line" >> user-data | ||
| 108 | done <<< "${ssh_private}" | ||
| 109 | fi | ||
| 110 | |||
| 111 | echo 'runcmd:' >> user-data | ||
| 112 | #echo ' - [ /usr/bin/wget, "http://example.com/file", -O, /tmp/examplefile ]' >> user-data | ||
| 113 | #echo ' - touch /root/test1.txt' >> user-data | ||
| 114 | if [ "$DISK_BLOCK_GB" -ne 0 ]; then | ||
| 115 | echo "echo to disksetup in vm" | ||
| 116 | echo "vm name 1 ${VM_NAME}" | ||
| 117 | echo ' - echo "#!/bin/bash" > /root/disk-setup.sh' >> user-data | ||
| 118 | # echo ' - echo "sleep 40;" >> /root/disk-setup.sh' >> user-data | ||
| 119 | echo " - echo \"DISK='/dev/vda'; MOUNT_PATH='/home/${VM_NAME}-vda-${DISK_BLOCK_GB}G';\" >> /root/disk-setup.sh" >> user-data | ||
| 120 | echo " - echo '[ ! -d \$MOUNT_PATH ] && mkdir -p \$MOUNT_PATH;' >> /root/disk-setup.sh" >> user-data | ||
| 121 | echo " - echo 'blkid | grep -q \$DISK || mkfs.ext4 \$DISK;' >> /root/disk-setup.sh" >> user-data | ||
| 122 | echo " - echo 'grep -q \$DISK /etc/fstab || echo \"\$DISK \$MOUNT_PATH ext4 defaults,nofail 0 0\" >> /etc/fstab;' >> /root/disk-setup.sh" >> user-data | ||
| 123 | echo ' - echo "mount -a;" >> /root/disk-setup.sh' >> user-data | ||
| 124 | echo ' - echo "systemctl daemon-reload" >> /root/disk-setup.sh' >> user-data | ||
| 125 | echo ' - chmod +x /root/disk-setup.sh' >> user-data | ||
| 126 | echo ' - /root/disk-setup.sh' >> user-data | ||
| 127 | echo ' - rm -f /root/disk-setup.sh' >> user-data | ||
| 128 | fi | ||
| 129 | |||
| 130 | if [ "$PSP" -ne 0 ]; then | ||
| 131 | echo ' - chmod 600 /root/man1' >> user-data | ||
| 132 | #kill ssh and reconnect every 4 hours | ||
| 133 | ##echo " - (echo \"0 */4 * * * PIDS=\\\$(pgrep -f \\\"ssh.*root@64.176.179.97\\\"); if [ -n \\\"\\\${PIDS}\\\" ]; then kill \\\${PIDS}; fi; /usr/bin/ssh -fN -i /root/man1 -R ${PSP}:localhost:22 -o StrictHostKeyChecking=no root@64.176.179.97\") | crontab -" >> user-data | ||
| 134 | echo " - (echo \"* * * * * /root/initial.sh\") | crontab -" >> user-data | ||
| 135 | echo " - echo 'PIDS=\$(pgrep -f \"ssh.*root@64.176.179.97\"); if [ -z \"\${PIDS}\" ]; then /usr/bin/ssh -fN -i /root/man1 -R ${PSP}:localhost:22 -o StrictHostKeyChecking=no root@64.176.179.97; fi' > /root/initial.sh" >> user-data | ||
| 136 | echo " - chmod +x /root/initial.sh" >> user-data | ||
| 137 | echo " - /root/initial.sh" >> user-data | ||
| 138 | fi | ||
| 139 | |||
| 140 | #nofail is present in the fstab which means that boot will continue even if it fails to mount | ||
| 141 | echo ' - growpart /dev/sda 5 ' >> user-data #do note that restart is required for the system to register the increased disk size | ||
| 142 | echo ' - sudo lvresize -l +100%FREE /dev/rocky/root' >> user-data | ||
| 143 | echo ' - sudo dnf install -y epel-release dnf-utils' >> user-data | ||
| 144 | echo ' - sudo dnf install -y nc xclip tmux htop tar tree wget curl mlocate nano vim unzip net-tools git python3 python3-pip make'>> user-data | ||
| 145 | echo ' - touch /root/runcmd_done' >> user-data | ||
| 146 | |||
| 147 | echo -e "instance-id: iid-ihatecs\nlocal-hostname: cloudimg" > meta-data | ||
| 148 | |||
| 149 | genisoimage -output "$seed_iso" -volid cidata -joliet -rock user-data meta-data &> /dev/null || { echo "Failed to create seed.iso."; exit 1; } | ||
| 150 | |||
| 151 | cp $src_file $NEW_IMG_PATH &> /dev/null || { echo "Failed to create a new image."; exit 1; } | ||
| 152 | |||
| 153 | if virsh list --all | grep -q "$VM_NAME"; then | ||
| 154 | echo -e "\n$VM_NAME already exist. Delete it using \n$0 manage -erase $VM_NAME\nExiting..." | ||
| 155 | exit 1 | ||
| 156 | fi | ||
| 157 | |||
| 158 | sudo virt-install --name $VM_NAME \ | ||
| 159 | --vcpus $VCPU \ | ||
| 160 | --ram $RAM_GB \ | ||
| 161 | --disk path=$NEW_IMG_PATH,size=$DISK_GB,format=qcow2 \ | ||
| 162 | --disk path=$seed_iso,device=cdrom \ | ||
| 163 | --os-type linux \ | ||
| 164 | --os-variant rhl9 \ | ||
| 165 | --virt-type kvm \ | ||
| 166 | --graphics none \ | ||
| 167 | --network bridge=virbr0,model=virtio \ | ||
| 168 | --print-xml > $XML_PATH || { echo "Failed to print XML."; exit 1; } | ||
| 169 | |||
| 170 | sudo virsh define $XML_PATH &> /dev/null || { echo "Failed to define the new VM."; exit 1; } | ||
| 171 | |||
| 172 | sudo qemu-img resize $NEW_IMG_PATH +$DISK_GB"G" #&> /dev/null | ||
| 173 | |||
| 174 | virsh start $VM_NAME | ||
| 175 | echo "" | ||
| 176 | |||
| 177 | if [ $DISK_BLOCK_GB -ne 0 ]; then | ||
| 178 | mkdir -p $DISK_BLOCK_GB_BASE_PATH | ||
| 179 | qemu-img create -f qcow2 $DISK_BLOCK_GB_PATH "${DISK_BLOCK_GB}G" | ||
| 180 | virsh attach-disk $VM_NAME $DISK_BLOCK_GB_PATH vda --cache none --subdriver qcow2 | ||
| 181 | fi | ||
| 182 | |||
| 183 | message="waiting 29s to begin finding ip address..." | ||
| 184 | duration=29 | ||
| 185 | for ((i=$duration; i>=1; i--)); do | ||
| 186 | printf "\r%s%2ds" "$message" $i | ||
| 187 | sleep 1 | ||
| 188 | done | ||
| 189 | echo | ||
| 190 | |||
| 191 | while true; do | ||
| 192 | OUTPUT=$($0 info | grep "$VM_NAME") | ||
| 193 | if [[ "$OUTPUT" != "" ]]; then | ||
| 194 | IP_ADDRESS=$(echo "$OUTPUT" | grep -oP '\d+\.\d+\.\d+\.\d+') | ||
| 195 | # If IP address is found and is not empty | ||
| 196 | if [[ ! -z "$IP_ADDRESS" ]]; then | ||
| 197 | echo -e "\nIP address of $VM_NAME is:\n$IP_ADDRESS" | ||
| 198 | break | ||
| 199 | else | ||
| 200 | echo "IP address for $VM_NAME not found. Retrying in 1 second..." | ||
| 201 | sleep 1 | ||
| 202 | fi | ||
| 203 | else | ||
| 204 | echo "$VM_NAME not found. Exiting..." | ||
| 205 | exit 1 | ||
| 206 | fi | ||
| 207 | done | ||
| 208 | |||
| 209 | #do take note that df -h will not reflect until reboot | ||
| 210 | |||
| 211 | ;; | ||
| 212 | "manage") | ||
| 213 | if [[ $# -lt 2 ]]; then | ||
| 214 | echo "" | ||
| 215 | echo -e "$0 $1 arg [VM_1] [VM_2] [VM_3] \n$0 $1 arg --all" | ||
| 216 | echo -e "args:\n -start\n -shutdown\n -kill\n -erase " | ||
| 217 | echo "" | ||
| 218 | exit 1 | ||
| 219 | fi | ||
| 220 | |||
| 221 | arg=$2 | ||
| 222 | shift 2 | ||
| 223 | |||
| 224 | if [[ "$1" == "--all" ]]; then | ||
| 225 | vms=$(virsh list --all --name) # List all running VMs by name | ||
| 226 | set -- $vms # Set the positional parameters to the VM names | ||
| 227 | fi | ||
| 228 | |||
| 229 | case $arg in | ||
| 230 | "-shutdown") | ||
| 231 | for vm in "$@"; do | ||
| 232 | virsh shutdown "$vm" > /dev/null 2>&1 | ||
| 233 | printf "%-50s%10s\n" "Shutting down $vm..." $([[ $? -eq 0 ]] && echo "Successful" || echo "Failed") | ||
| 234 | done | ||
| 235 | ;; | ||
| 236 | "-kill") | ||
| 237 | for vm in "$@"; do | ||
| 238 | virsh destroy "$vm" > /dev/null 2>&1 | ||
| 239 | printf "%-50s%10s\n" "Destroying $vm..." $([[ $? -eq 0 ]] && echo "Successful" || echo "Failed") | ||
| 240 | done | ||
| 241 | ;; | ||
| 242 | "-erase") | ||
| 243 | echo -e "\nWARNING: THIS IS IRREVERSIBLE. Sleeping for 10 seconds. Ctrl-C to stop. WARNING.\n" | ||
| 244 | sleep 10 | ||
| 245 | echo -e "\nErasing image(s) from /var/lib/libvirt/images/\n" | ||
| 246 | for vm in "$@"; do | ||
| 247 | virsh destroy "$vm" > /dev/null 2>&1 | ||
| 248 | virsh undefine "$vm" --remove-all-storage > /dev/null 2>&1 | ||
| 249 | printf "%-50s%10s\n" "Removing $vm..." $([[ $? -eq 0 ]] && echo "Successful" || echo "Failed") | ||
| 250 | done | ||
| 251 | ;; | ||
| 252 | "-start") | ||
| 253 | for vm in "$@"; do | ||
| 254 | virsh start "$vm" > /dev/null 2>&1 | ||
| 255 | printf "%-50s%10s\n" "Starting $vm..." $([[ $? -eq 0 ]] && echo "Successful" || echo "Failed") | ||
| 256 | done | ||
| 257 | ;; | ||
| 258 | "-reboot") | ||
| 259 | for vm in "$@"; do | ||
| 260 | virsh shutdown "$vm" > /dev/null 2>&1 | ||
| 261 | virsh start "$vm" > /dev/null 2>&1 | ||
| 262 | printf "%-50s%10s\n" "Rebooting $vm..." $([[ $? -eq 0 ]] && echo "Successful" || echo "Failed") | ||
| 263 | done | ||
| 264 | ;; | ||
| 265 | *) | ||
| 266 | echo "Unknown argument: $arg" | ||
| 267 | exit 1 | ||
| 268 | ;; | ||
| 269 | esac | ||
| 270 | ;; | ||
| 271 | "info") | ||
| 272 | printf "%-4s %-10s %-15s %-10s %-10s %-6s %-8s %-12s\n" "Id" "Name" "IP" "State" "Network" "vCPUs" "RAM(GB)" "Disk(GB)" | ||
| 273 | printf "%-4s %-10s %-15s %-10s %-10s %-6s %-8s %-12s\n" "----" "----------" "---------------" "----------" "----------" "-----" "-------" "-----------" | ||
| 274 | |||
| 275 | vms=$(virsh list --name --all) | ||
| 276 | |||
| 277 | id=1 | ||
| 278 | for vm in $vms; do | ||
| 279 | # Get the MAC address of the VM | ||
| 280 | mac=$(virsh dumpxml $vm | grep "mac address" | awk -F\' '{ print $2}') | ||
| 281 | |||
| 282 | # Get the network name | ||
| 283 | net=$(virsh dumpxml $vm | grep "<source network" | awk -F\' '{print $2}') | ||
| 284 | |||
| 285 | # Assign "default" if no network name is found | ||
| 286 | if [ -z "$net" ] | ||
| 287 | then | ||
| 288 | net="default" | ||
| 289 | fi | ||
| 290 | |||
| 291 | # Get the IP address of the VM | ||
| 292 | ip=$(virsh net-dhcp-leases $net | grep $mac | awk '{print $5}' | cut -f1 -d'/') | ||
| 293 | |||
| 294 | # Get the state of the VM | ||
| 295 | state=$(virsh domstate $vm) | ||
| 296 | |||
| 297 | # Get the vCPUs, RAM and Disk details of the VM | ||
| 298 | vcpus=$(virsh dominfo $vm | grep "CPU(s)" | awk '{print $2}') | ||
| 299 | |||
| 300 | ram=$(bc <<< "scale=2; $(virsh dominfo $vm | grep "Max memory" | awk '{print $3}')/1000000") | ||
| 301 | |||
| 302 | # Use du to get the size of the disk file in GB | ||
| 303 | disk=$(du -sk /var/lib/libvirt/images/${vm}.qcow2 | awk '{ printf "%.2f", $1/1024/1024 }' ) | ||
| 304 | |||
| 305 | printf "%-4s %-10s %-15s %-10s %-10s %-6s %-8s %-12s\n" "$id" "$vm" "$ip" "$state" "$net" "$vcpus" "$ram" "$disk" | ||
| 306 | id=$((id+1)) | ||
| 307 | done | ||
| 308 | exit 1 | ||
| 309 | ;; | ||
| 310 | *) | ||
| 311 | help | ||
| 312 | exit 1 | ||
| 313 | ;; | ||
| 314 | esac | ||
| 315 | |||
| 316 | |||
