Skip to content

Home Lab

Notes from my learning sessions

Menu
Menu

Orchestrating Guest VM(s) using simple scripts on KVM host.

Posted on October 10, 2021March 7, 2022 by sandeep

As a developer, I had a need to spin up VMs as and when required.  The more I started working on Kubernetes, the need to automate the spinning up of multiple VMs to bring up a setup for configuring a cluster.  There were several options, solutions already available to automate the same on KVM.  However, I had decided to quickly develop simple shell scripts which will help me achieve the same.

One of the needs was to delete any VM (domain) if already running with the same hostname and delete the associated image.

#!/bin/bash
if [ $# -lt 1 ]; then
echo "ERROR : hostname not specified"
exit 1
fi
info=`virsh list --all | grep "$1"`
if [ -z "$info" ]; then
# Not running - nothing to do
exit 0
fi
status=`echo $info | tr -s ' ' | cut -d" " -f3-`
echo " "
echo "Status : $status"
echo " "
if [ "$status" == "running" ]; then
virsh destroy $1
fi
virsh undefine $1
rm -rf /var/lib/libvirt/images/$1
rm -rf /opt/kvmmanager/templates/$1

Script for launching VM.  M

#!/bin/bash

# Assign defaults to variables
hostname="node"
storage="100G"
ram=12288
ip=""
gw="10.0.0.1"
ns="10.0.0.1"
cpus=4
os="centos"
version="7.0"
domain="datachronicles.net"
bridge="br2"
flavor=""
variant=""

# Parse the values passed in the command line

while getopts "h:s:r:i:g:n:c:o:v:d:b:f:" option; do
    case "${option}" in
        h)hostname=${OPTARG};;
        s)storage=${OPTARG};;
        r)ram=${OPTARG};;
        i)ip=${OPTARG};;
        g)gw=${OPTARG};;
        n)ns=${OPTARG};;
        c)cpus=${OPTARG};;
        o)os=${OPTARG};;
        v)version=${OPTARG};;
        d)domain=${OPTARG};;
        b)bridge=${OPTARG};;
        f)flavor=${OPTARG};;
    esac
done

# If IP is not provided then print usage and exit

if [ -z "$ip" ]; then
    echo "Usage : $0 -i <ipaddr> [-h <hostname>][-s <disksize>] [[-r <ram>][-c <cpus>] || [ -f <micro/mini/small/medium/large> ]] [-g <gateway>][-n <nameserver>][-o <os-distro>][-v <distro-version>][-d <domain>][-b <bridge-if>]"
    echo " "
    echo "Mandatory "
    echo "----------"
    echo " "
    echo " IP Address: Should be 'dhcp' or IP-Address in CIDR format"
    echo " "
    echo "Example values"
    echo "--------------"
    echo " "
    echo "Disk size : Example 20G or 40G"
    echo " "
    echo "Bridge interface : Host bridge name interface to be used"
    echo " "
    echo "Memory (in MB) : Example 16384"
    echo " "
    echo "OS-Distro : debian or ubuntu. Debaian versions - 11server, 11Mate, Ubuntu versions - 20.04, Centos - 7.0
    echo " "
    echo "Default values"
    echo "--------------"
    echo " "
    echo " Hostname: node, Domain: datachronicles.net"
    echo " CPUs: 4, RAM :16384, Disk size: 100G, Bridge Interface: br2"
    echo " Gateway: 10.0.0.1, Nameserver: 10.0.0.1"
    echo " OS-Distro: ubuntu, Version: 20.04"
    echo " "
    echo "Flavors"
    echo "-------"
    echo " micro - 1 vCPU and 3 GB RAM"
    echo " mini - 2 vCPU and 6 GB RAM"
    echo " small - 4 vCPU and 12 GB RAM"
    echo " large - 8 vCPU and 24 GB RAM"
    echo " xlarge - 16 vCPU and 48 GB RAM"
    echo " "
    exit 1
fi

# If flavor is specified then override values for vCPU and RAM
if [ ! -z "$flavor" ]; then
    if [ "$flavor" == "xlarge" ]; then
        cpus=16
        ram=49152
    elif [ "$flavor" == "large" ]; then
        cpus=8
        ram=24576
    elif [ "$flavor" == "small" ]; then
        cpus=4
        ram=12288
    elif [ "$flavor" == "mini" ]; then
        cpus=2
        ram=6144
    else
        cpus=1
        ram=3072
    fi
    echo " "
    echo "Flavor specified : $flavor. vCPUs = $cpus, RAM = $ram MB"
    echo " "
fi

echo " "
echo "----------------------------------------------------------------------------------------"
echo "Processing : $hostname."
echo " "

# If base image is not available - print error message and exit

if [ ! -f /opt/kvmmanager/images/$os/$os$version.qcow2 ]; then
    echo "Base image not found."
    exit 1
fi

# If VM with same hostname is running then destroy the VM and delete the image associated
# Destroy vm and undefine if already running or shutdown
deletevm $hostname

# Remove old images if any
if [ -d /var/lib/libvirt/images/$hostname ]; then
    rm -rf /var/lib/libvirt/images/$hostname
fi

# Clear any old working folders present
if [[ -d /opt/kvmmanager/templates/$hostname ]]; then
    rm -rf /opt/kvmmanager/templates/$hostname
fi

# For now variant for Debian is debiantesting, Ubuntu is ubuntu20.04, Centos is centos7

if [ "$os" == "centos" ]; then
    variant="centos07"
elif [ "$os" == "debian" ]; then
    variant="debiantesting"
elif [ "$os" == "ubuntu" ]; then
    variant="ubuntu20.04"
fi

# Create a folder for the VM in /var/lib/libvirt/images
mkdir -p /var/lib/libvirt/images/$hostname

# Create working folder for configurations and copy the templates
mkdir /opt/kvmmanager/templates/$hostname
if [ "$os" == "centos" ]; then
    intfile=/opt/kvmmanager/templates/$hostname/ifcfg-eth0
else
    intfile=/opt/kvmmanager/templates/$hostname/interfaces
fi

# Update templates with user input
if [ "$ip" == "dhcp" ]; then
    if [ "$os" == "centos" ]; then
        cp /opt/kvmmanager/templates/if/ifcfg-eth0-dhcp $intfile
    else
        cp /opt/kvmmanager/templates/if/interfaces-dhcp $intfile
    fi
else
    if [ "$os" == "centos" ]; then
        cp /opt/kvmmanager/templates/if/ifcfg-eth0 $intfile
    else
        cp /opt/kvmmanager/templates/if/interfaces $intfile
    fi
fi

# For centos we need to have ip and prefix identified
iponly=`echo $ip | cut -d"/" -f1`
prefix=`echo $ip | cut -d"/" -f2`

# Populate the configuration files with values provided

if [ "$ip" != "dhcp" ]; then
    sed -i 's@{{ip-address}}@'"$ip"'@' $intfile
    sed -i 's@{{gateway-ip}}@'"$gw"'@' $intfile
    # For centos
    sed -i 's@{{ip-only}}@'"$iponly"'@' $intfile
    sed -i 's@{{ip-prefix}}@'"$prefix"'@' $intfile
fi
sed -i 's@{{dns-server}}@'"$ns"'@' $intfile
echo " "

# Copy the base image to specific folder created for the VM and create an image
cp /opt/kvmmanager/images/$os/$os$version.qcow2 /var/lib/libvirt/images/$hostname/

qemu-img create -f qcow2 -F qcow2 -o backing_file=/var/lib/libvirt/images/$hostname/$os$version.qcow2 /var/lib/libvirt/images/$hostname/instance.qcow2

# Resize the image to storage size
qemu-img resize /var/lib/libvirt/images/"$hostname"/instance.qcow2 $storage

# Customize the VM with provided inputs
if [ "$os" == "centos" ]; then
    virt-customize -a /var/lib/libvirt/images/$hostname/instance.qcow2 --copy-in $intfile:/etc/sysconfig/network-scripts --hostname $hostname.$domain --timezone Asia/Kolkata --firstboot-command '/usr/local/bin/resizedisk'
else
    virt-customize -a /var/lib/libvirt/images/$hostname/instance.qcow2 --copy-in $intfile:/etc/network --hostname $hostname.$domain --timezone Asia/Kolkata --firstboot-command '/usr/local/bin/resizedisk'
fi

virt-install --connect qemu:///system --virt-type kvm --name $hostname --ram $ram --vcpus=$cpus --os-type linux --os-variant $variant --disk path=/var/lib/libvirt/images/$hostname/instance.qcow2,format=qcow2 --import --network bridge=$bridge --noautoconsole

virsh autostart $hostname

Finally, a script to read contents of say cluster.txt, with contents related to the metadata of VMs, one per line, in a predefined fixed format
root@blrs2:~/orchestrator# cat cluster.txt
#Host,DiskSize,Memory,CPU,IP,Gateway,DNSServer,BridgeInterface
ansible,100G,40960,8,10.99.3.1/16,10.99.0.1,10.99.9.1,br2

k8snode1,100G,40960,8,10.99.3.2/16,10.99.0.1,10.99.0.1,br3
k8snode2,100G,40960,8,10.99.3.3/16,10.99.0.1,10.99.0.1,br4
k8snode3,100G,40960,8,10.99.3.4/16,10.99.0.1,10.99.0.1,br2
k8snode4,100G,40960,8,10.99.3.5/16,10.99.0.1,10.99.0.1,br3

The cluster.sh script contents

#!/bin/bash
echo " "
if [ ! -f cluster.txt ]; then
echo "cluster.txt not found."
exit 1
fi
lc=`wc -l cluster.txt | grep -v "#" | tr -s ' ' | cut -d" " -f1`
if [[ -z $lc ]]; then
echo "Nothing to orchestrate"
exit 1
fi
if [[ $lc -lt 1 ]]; then
echo "Nothing to orchestrate"
exit 1
fi
while read oneline; do
if [ -z $oneline ]; then
echo " "
else
if [[ $oneline == \#* ]]; then
echo " "
else
host=`echo $oneline | cut -d"," -f1`
disk=`echo $oneline | cut -d"," -f2`
mem=`echo $oneline | cut -d"," -f3`
cpu=`echo $oneline | cut -d"," -f4`
ip=`echo $oneline | cut -d"," -f5`
gw=`echo $oneline | cut -d"," -f6`
ns=`echo $oneline | cut -d"," -f7`
br=`echo $oneline | cut -d"," -f8`
iponly=`echo $ip | cut -d/ -f1`

/root/orchestrator/orchestrate.sh -h $host -s $disk -r $mem -c $cpu -i $ip -g $gw -n $ns -b $br
fi
fi
done < cluster.txt

# Allow VMs to become ready for ssh login - Typically max 20 seconds
echo "Waiting for VMs startup to complete..."
sleep 30

# Typically with ubuntu images, the entry in /etc/hosts for the host is 127.0.1.1 - We need
# to have the ip address assigned

echo "Updating /etc/hosts in VM(s)"


while read oneline; do
if [ -z $oneline ]; then
echo " "
else
if [[ $oneline == \#* ]]; then
echo " "
else
echo " "
host=`echo $oneline | cut -d"," -f1`
ip=`echo $oneline | cut -d"," -f5`
iponly=`echo $ip | cut -d/ -f1`
ssh-keyscan -t rsa $iponly >> /root/.ssh/known_hosts 2>/dev/null;

# In the launched VM - delete 127.0.1.1 entry in /etc/hosts
cmddel="sed -i \"/$host/d\" /etc/hosts"
ssh -n $iponly "$cmddel"

# In the launched VM - add the /etc/host entry with user provided IP and hostname
cmdadd="echo $iponly $host.datachronicles.net $host >> /etc/hosts"
ssh -n $iponly "$cmdadd"
fi
fi
done < cluster.txt
 

Recent Posts

  • Ceph + KVM: 4. Orchestrating Ceph RBD backed VMs on KVM Hosts
  • Rabbit MQ Cluster + HAProxy + Keepalived
  • Install and configure MariaDB / Galera cluster
  • Ceph + KVM : 3. Installing KVM, cutsomized monitoring scripts
  • Ceph + KVM : 5. Service checks and CLI commands
  • Ceph + KVM : 2. Installation – Ceph Storage
  • Ceph + KVM : 1. Planning and preparing for Ceph Storage
  • Openstack Xena on Ubuntu 20.04 – Cinder
  • Preparing custom Debian 11 MATE image
  • Setup Ubuntu 20.04 repository mirror server

Archives

  • April 2025
  • March 2025
  • October 2024
  • September 2024
  • April 2022
  • March 2022
  • February 2022
  • December 2021
  • October 2021
  • September 2021
  • October 2020
  • February 2020
  • January 2020
  • December 2019
© 2025 Home Lab | Powered by Minimalist Blog WordPress Theme