kvm.sh

kvm.sh
#!/bin/bash
# Manage kvm guest.
# By mrco@xbits.net,2015-04-10 modified 2019-09-02
base_dir='/tank/kvm'
 
cd `dirname $0`
vms=`virsh list --all --name | sed '/^$/d'`
 
# 创建VMs
create_vms(){
    clear
    read -p "linux|windows?:" type
    read -p "版本(rhel7|win7|win2k8|win8.1~):" release
    read -p "虚拟机名:" name
    read -p "内存大小(M):" ram
    read -p "处理器核心数:" cores
    read -p "是否开启CPU超线程(y|n):" is_ht
    read -p "ISO文件路径:" iso
    # read -p "虚拟机磁盘文件名:" disk_name
    read -p "虚拟机磁盘格式(raw|qcow2):" disk_format
    read -p "虚拟机磁盘大小(xG):" disk_size
    read -p "NIC数量(默认1|2):" nic_num
    read -p "NIC1模式(默认br2|nat):" net1_type
    read -p "vnc端口号(5900~65535,-1自动分配):" port
    read -sp "vnc密码:" pass
    local virtio_driver=`readlink -f \`ls .|grep virtio-win*.iso\``
 
    if [[ is_ht == y ]];then
        threads=2    
    else
        threads=1
    fi
 
    if [[ ${nic_num} == 2 ]];then
        nic2="--network bridge=br1,model=virtio"
    fi
 
    if [[ "$net1_type" == "nat" ]];then
        nic1="network=default"
    else 
        nic1="bridge=br2"
    fi
 
    mkdir -p ${base_dir}/${name}
 
    if [[ "$disk_format" == "qcow2" ]];then
        qemu-img create -f qcow2 -o preallocation=metadata ${base_dir}/${name}/${name}.${disk_format} "$disk_size"G
    else
        disk_format=raw
        qemu-img create -f raw ${base_dir}/${name}/${name}.${disk_format} "$disk_size"G
    fi
 
    vcpus=$(($cores*$threads))
    #maxvcpus=`grep -c "model name" /proc/cpuinfo`
 
    virt-install --name ${name} \
        --clock offset=localtime \
        --ram ${ram} \
        --vcpus=${vcpus},sockets=1,cores=${cores},threads=${threads} \
        --arch=x86_64 \
        --os-type=${type} \
        --os-variant=${release} \
        --accelerate \
        --cdrom ${iso} \
        --boot menu=on,useserial=on \
        --disk path=${base_dir}/${name}/${name}.${disk_format},cache=writeback,bus=virtio,device=disk,driver_type=${disk_format} \
        --network ${nic1},model=virtio \
        ${nic2} \
        --graphics vnc,port=${port},listen=0.0.0.0,password=${pass} \
        --channel char_type='unix',target_type='virtio',name='org.qemu.guest_agent.0'
    exit 0
}
 
 
# 导出VM配置文件
dump_xml(){
    for i in ${vms};
    do
    local disk_file=`virsh domblklist $i|sed '1,2d'|grep -v '^$'|awk '{if($2!="-") print $2}'|head -n1`
    local vm_dir=`dirname $disk_file`
    cd ${vm_dir}
    virsh dumpxml ${i} > ${i}.xml
    printf "%-15s %-45s %-50s\n" ${i} ${disk_file} `readlink -f $i.xml`
    done
    exit 0
}
 
 
# 克隆
clone_vms(){
    clear
    echo 'virt-clone source虚拟机必须关机!'
    echo
    local vms=`virsh list --inactive --name`
    echo "$vms"
    echo
    read -p "要克隆的虚拟机名: " original
    read -p "新虚拟机名:" new
    read -p "克隆数量≥1: " num
    read -p "是否完成后开机? (y/n)" do_start
    local disk_file=`virsh domblklist $original|sed '1,2d'|grep -v '^$'|awk '{if($2!="-") print $2}'|head -n1`
    local disk_format=`qemu-img info $disk_file|grep "file format"|awk '{print $3}'`
 
    if [[ $num -gt 1 ]];then
        START=1
        for i in $(eval echo "{$START..$num}");do
            vmname="${new}-${i}"
            mkdir -p ${base_dir}/${vmname}
            virt-clone --original ${original} --name ${vmname} --file ${base_dir}/${vmname}/${vmname}.${disk_format}
            virsh dumpxml ${vmname} > ${base_dir}/${vmname}/${vmname}.xml
            if [[ $do_start == y ]];then
                virsh start ${vmname}
            fi
        done
    else
        vmname="${new}"
        mkdir -p ${base_dir}/${vmname}
        virt-clone --original ${original} --name ${vmname} --file ${base_dir}/${vmname}/${vmname}.${disk_format}
        virsh dumpxml ${vmname} > ${base_dir}/${vmname}/${vmname}.xml
        if [[ $do_start == y ]];then
              virsh start ${vmname}
        fi
    fi
}
 
 
# 快照
snapshot_vms(){
    vms=`virsh list --state-shutoff --name`
    echo "snapshot的虚拟机必须关机." 
    echo
    echo "$vms"
    echo
    read -p "要管理快照的虚拟机名:" be_snapshoted    
    #local state=`echo "$vms"|grep $be_snapshoted |awk '{print $3,$4}'`
    local snapshot_list=`virsh snapshot-list $be_snapshoted`
    local disk_file=`virsh domblklist $be_snapshoted|sed '1,2d'|grep -v '^$'|awk '{if($2!="-") print $2}'|head -n1`
    local disk_format=`qemu-img info $disk_file|grep "file format"|awk '{print $3}'`
    if [[ ${disk_format} == qcow2 ]];then
        #if [[ "$state" == "shut off" ]];then
            echo "Creating snapshot = C"
            echo "Listing snapshot = L"
            echo "Deleting snapshot = D"
            echo "Recovering snapshot = R"
            read choice
            if [[ "$choice" == "C" ]];then
                echo "$snapshot_list"
                read -p "新快照名:" snapshot_name
                virsh snapshot-create-as ${be_snapshoted} ${snapshot_name}
                [[ $? == 0 ]] && echo "快照创建完成."
 
            elif [[ "$choice" == "L" ]];then
                echo "$snapshot_list"
                read -p "要查看详情的快照名:" snapshot_info
                virsh snapshot-info ${be_snapshoted} ${snapshot_info}
 
            elif [[ "$choice" == "D" ]];then
                echo "$snapshot_list"
                read -p "要删除的快照名(多个空格分开):" del_snapshot
				for i in ${del_snapshot};do
					virsh snapshot-delete ${be_snapshoted} ${i}
					[[ $? == 0 ]] && echo "${i}快照删除完成."
				done
 
            elif [[ "$choice" == "R" ]];then
                echo "$snapshot_list"
                read -p "要恢复的快照名:" rec_snapshot
                virsh snapshot-revert ${be_snapshoted} ${rec_snapshot}
                [[ $? == 0 ]] && echo "快照恢复完成."
 
            fi
    else
        echo -e "\nERROR:snapshot仅支持qcow2格式.\n"
        echo -e "qemu-img convert 源文件 -O qcow2 源文件.qcow2"    
    fi
}
 
# Get the VMs info
GetVmInfo (){
    vms=`virsh list --state-running --name`
    clear
    for i in ${vms};do
    {
        echo -en "\033[32m ${i}: \n \033[0m"
        # 配置信息
        SpecInfo=`virsh dominfo ${i}`
        CPU=`echo "{$SpecInfo}"|grep -w 'CPU(s)'|sed 's/ //g'`
        RAM=`echo "{$SpecInfo}"|grep -w 'Used memory'`
        BlockInfo=`virsh domblklist $i --details|sed -e '1,2d' -e '$d'|awk '{print $2":",$3,$1,$4}'`
 
        # 网络信息(需要qemu-agent的支持)
        NetInfo=`virsh domifaddr ${i} --source agent 2> /dev/null | egrep -v 'error|lo|ipv6' | sed '1,2d'`
        interface=`echo "${NetInfo}"|awk '{print $1}'`
        mac=`echo "${NetInfo}"|awk '{print $2}'`
        ipv4=`echo "${NetInfo}"|awk '{print $4}'`
        vnc=`virsh domdisplay ${i}`
        # output infos
        echo -ne "\t${CPU}\n"
        echo -ne "\t${RAM}\n"
        echo "${BlockInfo}"|awk '{print "\t" $F}'
        printf "\t%-2s %-20s %-20s\n" ${interface}: ${mac} ${ipv4}
        printf "\t%-2s %-20s %-20s\n" ${vnc}
        echo $(printf %.s- {1..50})
    }
    done
}
 
 
# 附加磁盘到虚拟机
attach_disk(){
    clear
    local vms=`virsh list --all --name|grep -v '^$'`
    echo -ne "${vms}\n\n"
    echo -ne "Input VM name:\n"
    read target
    echo -ne "How many disks will you add?\n"
    read num
    echo -ne "size in GB?\n"
    read size
 
    # 定义数组,排除原虚拟机已有的盘符,防止冲突
    str1=`echo {a..z}`
    array_1=(${str1})
    array_2=(`virsh domblklist ${target}|sed '1,2d'|awk '{print $1}'|egrep -v 'hd'|awk '$0=$NF' FS=`)
    array_3=(`comm -3 <(printf '%s\n' "${array_1[@]}" | LC_ALL=C sort) <(printf '%s\n' "${array_2[@]}" | LC_ALL=C sort)`)
    # 循环创建磁盘并挂载        
    START=1
    for i in $(eval echo "{$START..$num}");do
        {
            local disk_file=`virsh domblklist ${target}|sed '1,2d'|grep -v '^$'|awk '{if($2!="-") print $2}'|head -n1`
            local disk_format=`qemu-img info --force-share ${disk_file}|grep "file format"|awk '{print $3}'`
            local disk_file_dir=`dirname ${disk_file}`
            # 创建并附加磁盘(永久生效)
            rand_number=`echo $RANDOM |md5sum |cut -c 1-6`
            # echo $i ${array_3[$i]} ${rand_number}
            qemu-img create -f ${disk_format} ${disk_file_dir}/disk_${rand_number} ${size}G
            # 如果循环数值和数组值不一致,系统lsblk得到的盘符和命令返回的盘符不一致,需要-1保持一致
            j=`echo ${i}-1|bc`
            virsh attach-disk ${target} ${disk_file_dir}/disk_${rand_number} vd${array_3[$j]} --subdriver ${disk_format} --persistent --live
        }
    done
}
 
 
 
# Adding a NIC to a VM.
add_nic(){
    echo "$vms"
    echo
    read -p "输入虚拟机名:" beadded
    read -p "网卡连接的网络(br1|br2):" bridge
    read -p "重启后消失,是否永久加入yes|no?" forever
    virsh attach-interface --domain ${beadded} --type bridge --source ${bridge} --model virtio --persistent
}
 
 
shutdown_all(){
    local vms=`virsh list --all --name --state-running`
    for i in ${vms};do
        virsh shutdown ${i}
    done
}
 
delete_vm(){
    read -p "要彻底删除的VM(多个空格分开,可序列如vm-{1..3}): " bedelete
	eval echo $bedelete
	read -p 'List Comfirm (Y/N)?' sure
	if [[ $sure == Y ]];then
		for i in $(eval echo $bedelete);do
			virsh destroy ${i} 2> /dev/null
			virsh undefine ${i}
			[[ -d ${base_dir}/${i} ]] && rm -rf ${base_dir}/${i} && echo -ne "$i deleted"'!\n'
		done
	fi
}    
 
# Help
help(){
    echo -ne "This shell helps manage KVM guests.\n"
}
 
echo -en "\033[32mType your choice: \033[0m"
echo
echo "1. Creating a VM."
echo "2. Dump all VMs' xml file."
echo "3. Clone VMs."
echo "4. Snapshoting."
echo "5. Get the VMs Info."
echo "6. attache 1 or multiple disk to a VM."
echo "7. Add a NIC to a VM."
echo "8. Shutdown all VMs."
echo "9. Delete VMs."
echo "10. Help."
read num
 
case "$num" in
    [1] ) (create_vms);;
    [2] ) (dump_xml);;
    [3] ) (clone_vms);;
    [4] ) (snapshot_vms);;
    [5] ) (GetVmInfo);;
    [6] ) (attach_disk);;
    [7] ) (add_nic);;
    [8] ) (shutdown_all);;
    [9] ) (delete_vm);;
    [10] ) (help);;
    *) (help);;
esac
exit 0
  • virtualization/kvm/manage_kvm.sh.txt
  • 最后更改: 2019/09/03 17:48
  • 由 mrco