setup libvirt VM by shell

Network setup

all code are here:

https://github.com/shaohef/kvm-script

Network Topology 

we only need to change the valuse here.  do not chagne other code.

REF:  libvirt: Network XML format

# network_name(net): bridge_name(br),forward_mode(mode),cidr_startip(cidr) #mode can be nat/route/isolated
NET_NAMES=(
'ansible_edge: asedgebr0' 
'smartcity_edge: scedgebr0' 
'smartcity_cloud: sccloudbr0' 
'ansible_cloud: ascloudbr0')

# [network_name]='host:interface_index host:interface_index'
declare -A NET_MACS
NET_MACS=(
[ansible_edge]='EDGE1:1 EDGE2:1 EDGE3:1 HUB:1'
[smartcity_edge]='EDGE1:2 EDGE2:2 EDGE3:2 HUB:2' 
[smartcity_cloud]='CLOUD:2 HUB:3' 
[ansible_cloud]='CLOUD:1')

IP=192.168.124.2

MODE=route # route or nat
#[host]="empty macs"
declare -A MACS=([EDGE1]="" [EDGE2]="" [EDGE3]="" [HUB]="" [CLOUD]="")

parser network structure utils 

# network_struct_parser 'ansible_edge: asedgebr0,nat ' 2
# network_struct_parser 'ansible_edge: asedgebr0' mode
function network_struct_parser(){
  # $1 struct string, $2 index(0,1,2,3) or key(net/br/mode/cidr)
  IFS=',: ' array=($1) ; unset IFS
  array[2]=${array[2]:-nat}
  declare -A MAP
  MAP=([net]=0 [br]=1 [mode]=2 [cidr]=3)
  # if [ -I "$2" ] ;
  [[ $2 =~ ^-?[0-9]+$ ]] && i=$2 || i=${MAP[$2]}
  echo ${array[$i]}
}

# network_struct_key 'ansible_edge: asedgebr0,nat '
function network_struct_key(){
  # $1 struct string
  echo ${1%:*}
}

# get_network_struct_item NET_NAMES ansible_cloud
function get_network_struct_item(){
# $1 NET_NAMES $2 key(network name)
for i in $(eval "echo ${!$1[@]}"); do
  v=$(eval "echo ${$1[i]}")
  NM=${v%:*}    BR=${v#*:}
  if [ "$NM" = "$2" ]; then
    echo $v
    break
  fi
done
}

# get_network_struct_value NET_NAMES ansible_cloud
function get_network_struct_value(){
# $1 NET_NAMES $2 key(network name)
for i in $(eval "echo ${!$1[@]}"); do
  v=$(eval "echo ${$1[i]}")
  NM=${v%:*}    VAL=${v#*:}
  if [ "$NM" = "$2" ]; then
    echo $VAL
    break
  fi
done
}

# grep_ip 'ansible_edge: asedgebr0,nat,192.168.1.1' cidr
function grep_ip(){
  match=$(grep -o '[0-9]{1,3}(.[0-9]{1,3}){3}' <<< $1)
  if [ $? -eq 0 ]; then
    echo $match
  fi
}

# network_struct_get_ip 'ansible_edge: asedgebr0,nat,192.168.1.1'
# network_struct_get_ip 'ansible_edge: asedgebr0,'
# network_struct_get_ip 'ansible_edge: asedgebr0,192.168.1.1'
function network_struct_get_ip(){
  # echo $(network_struct_parser "$1" mode)
  possible=$(grep_ip $(network_struct_parser "$1" mode))
  ip=${possible:-$(network_struct_parser "$1" cidr)}
  ip=$(grep_ip "$ip")
  echo $ip
}

# network_struct_get_mode 'ansible_edge: asedgebr0,nat,192.168.1.1'
# network_struct_get_mode 'ansible_edge: asedgebr0,'
# network_struct_get_mode 'ansible_edge: asedgebr0,192.168.1.1,nat'
# network_struct_get_mode 'ansible_edge: asedgebr0,192.168.1.1'
function network_struct_get_mode(){
  mode=$(network_struct_parser "$1" mode)
  possible=$(grep_ip $mode)
  [[ ! -z "$possible" ]] && mode=$(network_struct_parser "$1" cidr)
  mode=${mode:-nat}
  echo $mode
}

Generate a MAC address

REF:

# 33.9. Generating a new unique MAC address Red Hat Enterprise Linux 5 | Red Hat Customer Portal

function genmac(){
cat <<EOF | python3
import random
#
def randomMAC():  
  mac = [ 0x00, 0x16, 0x3e,
          random.randint(0x00, 0x7f),
          random.randint(0x00, 0xff),
          random.randint(0x00, 0xff) ]
  return ':'.join(map(lambda x: "%02x" % x, mac))
print(randomMAC())
EOF
}

Generate MAC pool 

# declare -A EDGE1_MACS EDGE2_MACS EDGE3_MACS HUB_MACS CLOUD_MACS

# MACS=(EDGE1_MACS EDGE2_MACS EDGE3_MACS HUB_MACS CLOUD_MACS)
# above 4.3 alpha version support nameref
# declare -A MACS=([EDGE1]="" [EDGE2]="" [EDGE3]="" [HUB]="" [CLOUD]="")
END=5
for i in "${!MACS[@]}"; do
  for j in $(seq 1 $END); do MACS[$i]=$(genmac)" "${MACS[$i]}; done 
  echo "export ${i}_MACS="${MACS[$i]}"" >> ~/.bashrc
done

 Get network Mac addess  Array

# declare -p NET_MACS
# get_net_macs NET_MACS ansible_edge
function get_net_macs(){
  # $1 net macs map NET_MACS, $2 net mames, $3 macs pool
  mac_idxs=$(eval "echo ${$1[$2]}") 
  # IFS=', ' read -r -a array <<< "$string" 
  # array=(${string//:/ }) 
  macstr=($mac_idxs)
  # return value
  ret=()
  for v in "${macstr[@]}"; do
    host=${v%%:*}     idx=${v##*:}    macs=${v%%:*}_MACS
    string=$(eval "echo ${$macs}")
    # IFS=', ' array=($countries)
    array=($string)
    # echo "${array[${!idx}]}"
    mac=$(eval "echo ${array[${idx}]}")
    ret+=($mac)
  done
  echo ${ret[@]}
}

 Get host name  Array  

# gen_host_array NET_MACS ansible_edge 
function gen_host_array(){
  # $1 net macs map NET_MACS, $2 net mames, $3 base ip addr
  mac_idxs=$(eval "echo ${$1[$2]}") 
  HOST=${mac_idxs//:/.}
  echo ${HOST,,}
}

 Get IP address  Array  

# inc_subnet 192.168.124.1 3
function inc_subnet(){
  SUBN=${1#*.*.}
  SUBN=${SUBN%.*}
  SUFFIX=${1##*.}
  echo ${1%.*.*}.$((${SUBN} + ${2})).${SUFFIX}
}

# inc_ipaddr 192.168.124.1 3
function inc_ipaddr(){
  echo ${1%.*}.$((${1##*.} + $2))
}

# gen_ip_array NET_MACS smartcity_edge 192.168.127.1
function gen_ip_array(){
  # $1 net macs map NET_MACS, $2 net mames, $3 base ip addr
  mac_idxs=$(eval "echo ${$1[$2]}") 
  macstr=($mac_idxs)
  len=${#macstr[@]}
  ret=()
  for (( i=0; i<$len; i++ )); do
    ip=$(inc_ipaddr $3 $i); 
    ret+=($ip)
  done
  echo ${ret[@]}
}

Get DHCP IP address allocation 

function get_dhcp_item(){
  # $1 MAC, $2 HOST, $3 DOMAIN, $4 IP
  DOM=${3/_/.}
  # (IFS=$';';  echo "      <host mac="$1" name="${2,,}.${DOM,,}.com" ip="$4"/>")
  echo "      <host mac="$1" name="${2,,}.${DOM,,}.com" ip="$4"/>"
}

# gen_dhcp_items NET_MACS ansible_edge 192.168.124.192
function gen_dhcp_items(){
  # $1 net macs map NET_MACS, $2 net mames, $3 base ip addr
  hosts=$(gen_host_array $1 $2)  
  macs=$(get_net_macs $1 $2)
  ips=$(gen_ip_array $1 $2 $3)
  ha=($hosts)   hm=($macs)  hi=($ips)
  ret=()
  for i in ${!ha[@]}; do
    itm=$(get_dhcp_item ${hm[$i]} ${ha[$i]} $2 ${hi[$i]})
    # declare -p itm
    ret+=("$itm")
    # echo ${ha[$i]} ${hm[$i]} ${hi[$i]}
  done
  # echo ${ret[@]}
  # declare -p ret
  ( IFS=$'
'; echo "${ret[*]}" )
}

 Generate network xml snippet files

# gen_networt_xml ansible_edge asedgebr0 192.168.124.1 $dhcp nat 
function gen_networt_xml(){
# $1 NET_NAME $2 BR NAME $3 IP $4 DHCP $5 MODE
str="<network>
  <name>$1</name>
  <bridge name="$2"/>
  <forward mode="${5}"/>
  <ip address="${3%.*}.1" netmask="255.255.255.0">
    <dhcp>
      <range start="${3%.*}.2" end="${3%.*}.192"/>
$4
    </dhcp>
  </ip>
</network>"

[[ ! -z ${5} && "isolated" =~ ${5,,} ]] && mode= || mode="  <forward mode="${5}"/>"

mkdir -p /tmp/vir_network
cat | tee /tmp/vir_network/$1.xml << EOF
<network>
  <name>$1</name>
  <bridge name="$2"/>
$mode
  <ip address="${3%.*}.1" netmask="255.255.255.0">
    <dhcp>
      <range start="${3%.*}.2" end="${3%.*}.192"/>
$4
    </dhcp>
  </ip>
</network>
EOF
# declare -p str
}


# gen_networt_xmls NET_NAMES NET_MACS 192.168.124.1 nat 
# gen_networt_xmls NET_NAMES NET_MACS 
function gen_networt_xmls(){
# $1 NET_NAMES $2 NET_MACS $3 IP $4 MODE: nat or route
IP=${3:-192.168.124.2}
for i in $(eval "echo ${!$1[@]}"); do
  v=$(eval "echo ${$1[i]}")
  NM=${v%:*}    BR=$(network_struct_parser "$v" br)
  ip=$(inc_subnet $IP $i)
  cidr_ip=$(network_struct_get_ip "$v")
  ip=${cidr_ip:-$ip}
  dhcp=$(gen_dhcp_items $2 $NM $ip)
  mode=$(network_struct_get_mode "$v")
  gen_networt_xml $NM $BR $ip "$dhcp" ${4:-$mode}
done
}

Network Operation

create 

virsh net-create /tmp/vir_network.xml
virsh net-list

ip a

 delete by 

virsh net-destroy

VM Operation

 download the expect images

CentOS 7 Cloud images

CentOS 8 Cloud images

Ubuntu Cloud Images - the official Ubuntu images for public clouds, Openstack, KVM and LXD

basic value 

REF: libvirt: Domain XML format

VM_NAME=smartcity_cloud
VM_MEMSIZE_G=10
VM_VCPUS=6
KVM=/usr/libexec/qemu-kvm
IMG=/var/lib/libvirt/images/edge2.qcow2

Get host mac address and network infomatin. 

REF: How to sort an array in Bash - Stack Overflow

# get_host_macs NET_MACS HUB
function get_host_macs(){
  # $1 net macs map NET_MACS, $2 hostname, $3 macs pool
  VAL=${2^^}
  ret=()
  for i in $(eval "echo ${!$1[@]}"); do
    v=$(eval "echo ${$1[$i]}")
    match=$(grep -o "$VAL:[[:digit:]]{1,4}" <<< $v)
    if [ $? -ne 0 ]; then
      continue
    fi
    idx=${match#*:}
    macs=${match%%:*}_MACS
    string=$(eval "echo ${$macs}")
    array=($string)
    # echo "${array[${!idx}]}"
    mac=$(eval "echo ${array[${idx}]}")
    # 0 host, 1 index, 2 network, 3 mac
    ret+=("$VAL $idx $i $mac,")
  done
  # IFS=$'
' sorted=($(sort <<<"${ret[*]}")); unset IFS
  # readarray -t sorted < <(for a in "${ret[@]}"; do echo "$a"; done | sort)
  sorted=($(printf '%s
' "${ret[@]}"|sort))
  echo ${sorted[@]}
}

# get_network_brige NET_NAMES ansible_cloud
function get_network_brige(){
# $1 NET_NAMES $2 network name
for i in $(eval "echo ${!$1[@]}"); do
  v=$(eval "echo ${$1[i]}")
  NM=${v%:*}    BR=$(awk -F"[, :]" '{print $1}' <<< ${v#*: })
  if [ "$NM" = "$2" ]; then
    echo $BR
    break
  fi
done
}

Generate network interface xml snippet files

# gen_interface_xml testnet 11.22.33.44.55.77 br0
function gen_interface_xml(){
IFMODEL=${IFMODEL:-rtl8139}    #"virtio"
  # $1 network name in NET_MACS, $2 mac address, $3 Bridge name
  #    <target dev='vnet1'/>
  #    <alias name='net0'/>
  str="    <interface type='network'>
      <mac address='$2'/>
      <source network='$1' bridge='$3'/>
      <model type='$IFMODEL'/>
    </interface>"
# declare -p str
echo "$str"
}

# gen_interface_xmls NET_MACS HUB NET_NAMES
function gen_interface_xmls(){
  # $1 net macs map NET_MACS, $2 hostname, $3 network-bridge NET_NAMES
  ret=()
  macs=$(get_host_macs $1 $2)
  IFS=',' maca=($macs); unset IFS
  for i in ${!maca[@]}; do
    vs=${maca[$i]}
    va=($vs)
    # 0 host, 1 index, 2 network, 3 mac
    # echo ${va[1]} ${va[2]}
    BR=$(get_network_brige $3 ${va[2]})
    xml=$(gen_interface_xml ${va[2]} ${va[3]} $BR)
    ret+=("$xml")
  done
  ( IFS=$'
'; echo "${ret[*]}" )
  # echo "${ret[@]}"
}

Generate VM xml 

# get_vm_xml NET_MACS cloud NET_NAMES
function get_vm_xml(){
# $1 net macs map NET_MACS, $2 hostname, $3 network-bridge NET_NAMES

The whole code as follow:

V1.2

function null_exit(){ [[ -z "$1" ]] && exit 1; }
# BASEIMG=/var/lib/libvirt/images/test.qcow2

# get_vm_xml NET_MACS cloud NET_NAMES
function get_vm_xml(){
# https://stackoverflow.com/questions/9893667/is-there-a-way-to-write-a-bash-function-which-aborts-the-whole-execution-no-mat
trap "exit 1" TERM
export TOP_PID=$$
# kill -s TERM $TOP_PID

echo "BASEIMG=$BASEIMG"
null_exit $BASEIMG
echo "VM_NAME=$VM_NAME"
null_exit $VM_NAME

VM_MEMSIZE_G=${VM_MEMSIZE_G:-10}
VM_VCPUS=${VM_VCPUS:-4}
echo "VM_MEMSIZE_G=$VM_MEMSIZE_G VM_VCPUS=$VM_VCPUS"

IMG=${BASEIMG%/*}/$2.${BASEIMG#*.}
cp $BASEIMG $IMG
virt-edit -a $IMG /etc/hostname -e "s/^.*$/$2/"

ifc=$(virt-ls -a $IMG  /etc/sysconfig/network-scripts/ |grep ifcfg-ens* |head -n 1)
virt-edit -a $IMG /etc/sysconfig/network-scripts/ifc -e 's/^BOOTPROTO=.*/BOOTPROTO=dhcp/'

# $1 net macs map NET_MACS, $2 hostname, $3 network-bridge NET_NAMES
mkdir -p /tmp/vir_domain
INTREFCAE=$(gen_interface_xmls $1 $2 $3)
cat | tee /tmp/vir_domain/${VM_NAME}.xml << EOF
<domain type='kvm' id='2'>
  <name>$VM_NAME</name>
  <memory unit='GiB'>$VM_MEMSIZE_G</memory>
  <vcpu placement='static'>$VM_VCPUS</vcpu>
  <resource>
    <partition>/machine</partition>
  </resource>
  <os>
    <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
    <apic/>
  </features>
  <cpu mode='custom' match='exact' check='full'>
    <model fallback='forbid'>Skylake-Server-IBRS</model>
    <feature policy='require' name='md-clear'/>
    <feature policy='require' name='spec-ctrl'/>
    <feature policy='require' name='ssbd'/>
    <feature policy='require' name='hypervisor'/>
    <feature policy='disable' name='arat'/>
  </cpu>
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>
  </pm>
  <devices>
    <emulator>$KVM</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='$IMG'/>
      <backingStore/>
      <target dev='vda' bus='virtio'/>
      <alias name='virtio-disk0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu'/>
      <target dev='hda' bus='ide'/>
      <readonly/>
      <alias name='ide0-0-0'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <controller type='usb' index='0' model='ich9-ehci1'>
      <alias name='usb'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci1'>
      <alias name='usb'/>
      <master startport='0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci2'>
      <alias name='usb'/>
      <master startport='2'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci3'>
      <alias name='usb'/>
      <master startport='4'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
    </controller>
    <controller type='pci' index='0' model='pci-root'>
      <alias name='pci.0'/>
    </controller>
    <controller type='ide' index='0'>
      <alias name='ide'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
$INTREFCAE
    <serial type='pty'>
      <source path='/dev/pts/5'/>
      <target type='isa-serial' port='0'>
        <model name='isa-serial'/>
      </target>
      <alias name='serial0'/>
    </serial>
    <console type='pty' tty='/dev/pts/5'>
      <source path='/dev/pts/5'/>
      <target type='serial' port='0'/>
      <alias name='serial0'/>
    </console>
    <input type='mouse' bus='ps2'>
      <alias name='input0'/>
    </input>
    <input type='keyboard' bus='ps2'>
      <alias name='input1'/>
    </input>
    <video>
      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
      <alias name='video0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>
    <memballoon model='virtio'>
      <alias name='balloon0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
    </memballoon>
  </devices>
  <seclabel type='dynamic' model='selinux' relabel='yes'>
    <label>system_u:system_r:svirt_t:s0:c263,c892</label>
    <imagelabel>system_u:object_r:svirt_image_t:s0:c263,c892</imagelabel>
  </seclabel>
  <seclabel type='dynamic' model='dac' relabel='yes'>
    <label>+107:+107</label>
    <imagelabel>+107:+107</imagelabel>
  </seclabel>
</domain>
EOF
}

V1.1

  1 # get_vm_xml NET_MACS cloud NET_NAMES
  2 function get_vm_xml(){
  3 # $1 net macs map NET_MACS, $2 hostname, $3 network-bridge NET_NAMES
  4 mkdir -p /tmp/vir_domain
  5 INTREFCAE=$(gen_interface_xmls $1 $2 $3)
  6 cat | tee /tmp/vir_domain/${VM_NAME}.xml << EOF
  7 <domain type='kvm' id='2'>
  8   <name>$VM_NAME</name>
  9   <memory unit='GiB'>$VM_MEMSIZE_G</memory>
 10   <vcpu placement='static'>$VM_VCPUS</vcpu>
 11   <resource>
 12     <partition>/machine</partition>
 13   </resource>
 14   <os>
 15     <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
 16     <boot dev='hd'/>
 17   </os>
 18   <features>
 19     <acpi/>
 20     <apic/>
 21   </features>
 22   <cpu mode='custom' match='exact' check='full'>
 23     <model fallback='forbid'>Skylake-Server-IBRS</model>
 24     <feature policy='require' name='md-clear'/>
 25     <feature policy='require' name='spec-ctrl'/>
 26     <feature policy='require' name='ssbd'/>
 27     <feature policy='require' name='hypervisor'/>
 28     <feature policy='disable' name='arat'/>
 29   </cpu>
 30   <clock offset='utc'>
 31     <timer name='rtc' tickpolicy='catchup'/>
 32     <timer name='pit' tickpolicy='delay'/>
 33     <timer name='hpet' present='no'/>
 34   </clock>
 35   <on_poweroff>destroy</on_poweroff>
 36   <on_reboot>restart</on_reboot>
 37   <on_crash>destroy</on_crash>
 38   <pm>
 39     <suspend-to-mem enabled='no'/>
 40     <suspend-to-disk enabled='no'/>
 41   </pm>
 42   <devices>
 43     <emulator>$KVM</emulator>
 44     <disk type='file' device='disk'>
 45       <driver name='qemu' type='qcow2'/>
 46       <source file='$IMG'/>
 47       <backingStore/>
 48       <target dev='vda' bus='virtio'/>
 49       <alias name='virtio-disk0'/>
 50       <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
 51     </disk>
 52     <disk type='file' device='cdrom'>
 53       <driver name='qemu'/>
 54       <target dev='hda' bus='ide'/>
 55       <readonly/>
 56       <alias name='ide0-0-0'/>
 57       <address type='drive' controller='0' bus='0' target='0' unit='0'/>
 58     </disk>
 59     <controller type='usb' index='0' model='ich9-ehci1'>
 60       <alias name='usb'/>
 61       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
 62     </controller>
 63     <controller type='usb' index='0' model='ich9-uhci1'>
 64       <alias name='usb'/>
 65       <master startport='0'/>
 66       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/>
 67     </controller>
 68     <controller type='usb' index='0' model='ich9-uhci2'>
 69       <alias name='usb'/>
 70       <master startport='2'/>
 71       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
 72     </controller>
 73     <controller type='usb' index='0' model='ich9-uhci3'>
 74       <alias name='usb'/>
 75       <master startport='4'/>
 76       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
 77     </controller>
 78     <controller type='pci' index='0' model='pci-root'>
 79       <alias name='pci.0'/>
 80     </controller>
 81     <controller type='ide' index='0'>
 82       <alias name='ide'/>
 83       <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
 84     </controller>
 85 $INTREFCAE
 86     <serial type='pty'>
 87       <source path='/dev/pts/5'/>
 88       <target type='isa-serial' port='0'>
 89         <model name='isa-serial'/>
 90       </target>
 91       <alias name='serial0'/>
 92     </serial>
 93     <console type='pty' tty='/dev/pts/5'>
 94       <source path='/dev/pts/5'/>
 95       <target type='serial' port='0'/>
 96       <alias name='serial0'/>
 97     </console>
 98     <input type='mouse' bus='ps2'>
 99       <alias name='input0'/>
100     </input>
101     <input type='keyboard' bus='ps2'>
102       <alias name='input1'/>
103     </input>
104     <video>
105       <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
106       <alias name='video0'/>
107       <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
108     </video>
109     <memballoon model='virtio'>
110       <alias name='balloon0'/>
111       <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
112     </memballoon>
113   </devices>
114   <seclabel type='dynamic' model='selinux' relabel='yes'>
115     <label>system_u:system_r:svirt_t:s0:c263,c892</label>
116     <imagelabel>system_u:object_r:svirt_image_t:s0:c263,c892</imagelabel>
117   </seclabel>
118   <seclabel type='dynamic' model='dac' relabel='yes'>
119     <label>+107:+107</label>
120     <imagelabel>+107:+107</imagelabel>
121   </seclabel>
122 </domain>
123 EOF
124 }
View Code

V1

  1 # get_vm_xml NET_MACS cloud NET_NAMES
  2 function get_vm_xml(){
  3 # $1 net macs map NET_MACS, $2 hostname, $3 network-bridge NET_NAMES
  4 
  5 INTREFCAE=$(gen_interface_xmls $1 $2 $3)
  6 cat | tee /tmp/vir_VM.xml << EOF
  7 <domain type='kvm' id='2'>
  8   <name>$VM_NAME</name>
  9   <memory unit='GiB'>$VM_MEMSIZE_G</memory>
 10   <vcpu placement='static'>$VM_VCPUS</vcpu>
 11   <resource>
 12     <partition>/machine</partition>
 13   </resource>
 14   <os>
 15     <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
 16     <boot dev='hd'/>
 17   </os>
 18   <features>
 19     <acpi/>
 20     <apic/>
 21   </features>
 22   <cpu mode='custom' match='exact' check='full'>
 23     <model fallback='forbid'>Skylake-Server-IBRS</model>
 24     <feature policy='require' name='md-clear'/>
 25     <feature policy='require' name='spec-ctrl'/>
 26     <feature policy='require' name='ssbd'/>
 27     <feature policy='require' name='hypervisor'/>
 28     <feature policy='disable' name='arat'/>
 29   </cpu>
 30   <clock offset='utc'>
 31     <timer name='rtc' tickpolicy='catchup'/>
 32     <timer name='pit' tickpolicy='delay'/>
 33     <timer name='hpet' present='no'/>
 34   </clock>
 35   <on_poweroff>destroy</on_poweroff>
 36   <on_reboot>restart</on_reboot>
 37   <on_crash>destroy</on_crash>
 38   <pm>
 39     <suspend-to-mem enabled='no'/>
 40     <suspend-to-disk enabled='no'/>
 41   </pm>
 42   <devices>
 43     <emulator>$KVM</emulator>
 44     <disk type='file' device='disk'>
 45       <driver name='qemu' type='qcow2'/>
 46       <source file='$IMG'/>
 47       <backingStore/>
 48       <target dev='vda' bus='virtio'/>
 49       <alias name='virtio-disk0'/>
 50       <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
 51     </disk>
 52     <disk type='file' device='cdrom'>
 53       <driver name='qemu'/>
 54       <target dev='hda' bus='ide'/>
 55       <readonly/>
 56       <alias name='ide0-0-0'/>
 57       <address type='drive' controller='0' bus='0' target='0' unit='0'/>
 58     </disk>
 59     <controller type='usb' index='0' model='ich9-ehci1'>
 60       <alias name='usb'/>
 61       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
 62     </controller>
 63     <controller type='usb' index='0' model='ich9-uhci1'>
 64       <alias name='usb'/>
 65       <master startport='0'/>
 66       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/>
 67     </controller>
 68     <controller type='usb' index='0' model='ich9-uhci2'>
 69       <alias name='usb'/>
 70       <master startport='2'/>
 71       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
 72     </controller>
 73     <controller type='usb' index='0' model='ich9-uhci3'>
 74       <alias name='usb'/>
 75       <master startport='4'/>
 76       <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
 77     </controller>
 78     <controller type='pci' index='0' model='pci-root'>
 79       <alias name='pci.0'/>
 80     </controller>
 81     <controller type='ide' index='0'>
 82       <alias name='ide'/>
 83       <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
 84     </controller>
 85 $INTREFCAE
 86     <serial type='pty'>
 87       <source path='/dev/pts/5'/>
 88       <target type='isa-serial' port='0'>
 89         <model name='isa-serial'/>
 90       </target>
 91       <alias name='serial0'/>
 92     </serial>
 93     <console type='pty' tty='/dev/pts/5'>
 94       <source path='/dev/pts/5'/>
 95       <target type='serial' port='0'/>
 96       <alias name='serial0'/>
 97     </console>
 98     <input type='mouse' bus='ps2'>
 99       <alias name='input0'/>
100     </input>
101     <input type='keyboard' bus='ps2'>
102       <alias name='input1'/>
103     </input>
104     <video>
105       <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
106       <alias name='video0'/>
107       <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
108     </video>
109     <memballoon model='virtio'>
110       <alias name='balloon0'/>
111       <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
112     </memballoon>
113   </devices>
114   <seclabel type='dynamic' model='selinux' relabel='yes'>
115     <label>system_u:system_r:svirt_t:s0:c263,c892</label>
116     <imagelabel>system_u:object_r:svirt_image_t:s0:c263,c892</imagelabel>
117   </seclabel>
118   <seclabel type='dynamic' model='dac' relabel='yes'>
119     <label>+107:+107</label>
120     <imagelabel>+107:+107</imagelabel>
121   </seclabel>
122 </domain>
123 EOF
124 }
View Code

Strat VM

HOST=cloud 
IMG=/var/lib/libvirt/images/$HOST.qcow2

VM_NAME=smartcity_$HOST
VM_MEMSIZE_G=10
VM_VCPUS=6

get_vm_xml NET_MACS $HOST NET_NAMES
virsh create /tmp/vir_domain/$VM_NAME.xml
virsh list

XML tools to parser 

REF:

xmllint - Native shell command set to extract node value from XML - Stack Overflow  

xmllint displays value of second attribute if first one matches - Unix & Linux Stack Exchange

sed - How do I extract a single attribute from an XML file? - Ask Ubuntu

bash extract xml attribute value using xmllint - Stack Overflow

Pre-define expression 

XML_IFC_EXP="//*[local-name()='interface']/address/@slot"

Parser XML 

XML_IFC_EXP="//*[local-name()='interface']/address/@slot" 

# get_xml_attr_value $XML_IFC_EXP smartcity_cloud.xml
function get_xml_attr_value(){
ret=()
str=$(xmllint --xpath "$1" $2)
entries=($(echo ${str}))
for entry in "${entries[@]}"; do
  result=$(echo $entry | awk -F'[="]' '!/>/{print $(NF-1)}')
  ret+=("$result")
done
echo ${ret[@]}
}

function get_xml_elem_text(){

}

Inject interface script 

NIC_PREFIX=ens 
virsh list --all

# inject_ifc_script smartcity_edge3 
function inject_ifc_script(){
# $1 domain name
[[ "running" == "$(virsh domstate $1)" ]] && echo "The vm in running, exit" && return 1
mkdir -p /tmp/vir_domain
virsh dumpxml $1 > /tmp/vir_domain/vm_tmp.xml

NIC_PREFIX=${NIC_PREFIX:-ens}
XML_IFC_EXP_DEF="//*[local-name()='interface']/address/@slot"
XML_IFC_EXP=${XML_IFC_EXP:-$XML_IFC_EXP_DEF}
dev=$(get_xml_attr_value $XML_IFC_EXP /tmp/vir_domain/vm_tmp.xml)
build_list=($dev)

for i in "${build_list[@]:1}"; do
   ifc="${NIC_PREFIX}${i#0x0*}"
   gen_ifcfg $ifc
   virt-copy-in -d $1 /tmp/vir_domain/ifcfg-$ifc /etc/sysconfig/network-scripts
done
}

function gen_ifcfg(){
BOOTPROTO=${BOOTPROTO:-none}
cat > /tmp/vir_domain/ifcfg-$1 << EOF
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=${BOOTPROTO}
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=$1
# UUID=ccf2fab2-87aa-4279-b7dc-e8f3f2b87d12
DEVICE=$1
ONBOOT=yes
# IPADDR=192.168.122.3
# NETMASK=255.255.255.0
# GATEWAY=192.168.122.1
EOF
}

 Only set one interface as the dhcp bootproto 

virsh list --all

inject_ifc_script $dom

 Set VM interface model 

# set_interface_model domain index model
function set_interface_model(){
# $1 domain $2 device index(can be "all") $3 model
num=${2:-all}
model=${3:-virtio}
virt-xml $1 --edit $num --print-diff --network model=$model
}

 Set VM Host Name

function set_hostname() {
# $1 domain name $2 host name
[[ "running" == "$(virsh domstate $1)" ]] && echo "The vm in running, exit" && return 1
virt-edit -d $1 /etc/hostname -e "s/^.*$/$2/"
}

Resize VM Disk(centos)

we can run this command and resize it in VM VMware虚拟机中CentOS7的硬盘空间扩容 - 华为云 (huaweicloud.com)

qemu-img resize /var/lib/libvirt/images/test20g.qcow2 +10G

a simple way(interactive)

# https://www.huaweicloud.com/articles/efd2b3302420a70e14adae31cf87f9b0.html

# resize_vm_disk /var/lib/libvirt/images/tmp.qcow2 newname
function resize_vm_disk () {
  # $1 old disk name $2 new disk $3 total size $4 partition $5 volume
  if [ -z "$1" ]; then
    ls /var/lib/libvirt/images/ 
    echo "resize_vm_disk /var/lib/libvirt/images/${olddisk}.qcow2 ${newdisk} 30G"
    return 1
  fi
  new=${1%/*}/${2}.qcow2
  size=${3:-20G}
  disk=${4:-/dev/sda2}
  lv_disk=${5:-/dev/centos/root}
  virt-filesystems --long -h --all -a $1
  qemu-img create -f qcow2 -o preallocation=metadata $new $size
  # https://blog.csdn.net/tutucute0000/article/details/38414449
  virt-resize --expand $disk --LV-expand $lv_disk $1 $new
  virt-filesystems --long -h --all -a $new
  echo "new qcow2 disk generated: $new"
  echo "NOTE: For xfs filesystem, please resize volume using CLI inside VM manfully "
  echo "  virt-rescue -a $new -i"
  echo "  chroot /sysroot"
  echo "  vgdisplay"
  echo "  lvextend -l +100%FREE /dev/mapper/centos-root"
  echo "  xfs_growfs /dev/mapper/centos-root"
  echo "  df -h"
  echo "after exit, please recheck: "
  echo "  virt-filesystems --long -h --all -a $new"
}

 auto resize

v1.1

  1 function gen_resize_lvm_script(){
  2 FILE=/tmp/vir_domain/resize_lvm.sh
  3 cat > $FILE << EOF
  4 # vgdisplay
  5 lvextend -l +100%FREE ${2:-/dev/mapper/centos-root} >> /root/boot1.log
  6 ${1:-xfs_growfs} ${2:-/dev/mapper/centos-root} >> /root/boot1.log
  7 # df -h
  8 EOF
  9 echo "$FILE"
 10 }
 11 
 12 # get_dom_1st_disk domain_name
 13 function get_dom_1st_disk(){
 14     # $1 domain name
 15     FIRST_DISK_EXP='//disk[1][@device="disk"]/source/@file'
 16     tmpxml=/tmp/vir_domain/tmp.xml
 17     mkdir -p /tmp/vir_domain
 18     virsh dumpxml $1 > $tmpxml
 19     img=$(xmllint --xpath $FIRST_DISK_EXP $tmpxml | awk -F'[="]' '!/>/{print $(NF-1)}')
 20     echo "$img"
 21 }
 22 
 23 
 24 function check_resize_dom_lvm_1st_arg(){
 25   if [ -z "$1" ]; then
 26     virsh list --all
 27     echo "usage:"
 28     echo "  resize_dom_lvm domain|file size_G disk volume script_file "
 29     return 1
 30   fi
 31 
 32   if [[ ! -f "$1" ]] ; then
 33      virsh dominfo $1
 34      ret=$?
 35      [[ $ret -ne 0 ]] && return $ret
 36   fi
 37 }
 38 
 39 
 40 function check_resize_dom_lvm_2st_arg(){
 41   if [ -z "$2" ]; then
 42     echo "Error: missing the size parameters($2): "
 43     virt-filesystems --long -h --all -a $1
 44     echo "Get the resize disk($3) and lvm($4) from above information"
 45     echo "And generate resize script by this command:"
 46     echo "  gen_resize_lvm_script"
 47     echo "Check the script is right"
 48     return 1
 49   fi
 50 }
 51 
 52 function parser_disk_info(){
 53   # $1 image
 54   img=$1
 55   # 0 last partition, 1 last pv 2. last vg 3. last vg parent 4. root lv, 5. file type 6. is lvm (check pv)
 56   ret=()
 57   fsys=$(virt-filesystems --long -h --all -a $img)
 58 
 59   # ---------------------- 0 last partition -----------------------
 60   partn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9 ]*partition"|sort|tail -n 1)
 61   # partndev=$(echo "$partn" | awk '{print $1}')
 62   partndev=${partn%% *}
 63   ret+=("$partndev")
 64   # ---------------------- 1 last pv        -----------------------
 65   pvn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9 ]*pv"|sort|tail -n 1)
 66   pvndev=$(echo "$pvn" | awk '{print $1}')
 67   # disk=${3:-/dev/sda2}
 68   ret+=("$pvndev")
 69   # pvndev=${3:-$pvndev}
 70 
 71   # ---------------------- 2 last vg        -----------------------
 72   vgn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9]*[[:space:]]*vg.*"|sort|tail -n 1)
 73   vgdev=$(echo "$vgn" | awk '{print $1}')
 74   ret+=("$vgdev")
 75 
 76   # ---------------------- 3 last vg parent -----------------------
 77   vgnp=${vgn##* }
 78   ret+=("$vgnp")
 79 
 80   # ---------------------- 4 root lv    -----------------------
 81   lv=$(echo "$fsys" |grep "${vgdev}/root[[:space:]]*.*${vgdev}"|sort|tail -n 1)
 82   lvroot=$(echo "$lv" | awk '{print $1}')
 83   # lvroot=${4:-$lvroot}
 84   ret+=("$lvroot")
 85 
 86   # ---------------------- 5 file type -----------------------
 87   fs=$(echo "$fsys" |grep "${lvroot}[[:space:]]*filesystem")
 88   fstype=$(echo "$fs" | awk '{print $3}')
 89   ret+=("$fstype")
 90 
 91   # ---------------------- 6 is lvm   -----------------------
 92   declare -p ret
 93   # echo $pvn, $vgn, $fs, $vgdev
 94   echo $pvndev, $lvroot, $fstype
 95 }
 96 
 97 
 98 
 99 function parser_disk_lv_info(){
100   # $1 image
101   img=$1
102   # 0 last partition, 1 last pv 2. last vg 3. last vg parent 4. root vg, 5. file type 6. is lvm (check pv)
103   ret=()
104   fsys=$(virt-filesystems --long -h --all -a $img)
105   vgn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9]*[[:space:]]*vg[[:space:]]*.*/dev/[A-Za-z1-9]*"|sort|tail -n 1)
106   vgdev=${vgn%% *}
107   vgnp=${vgn##* }
108   lv=$(echo "$fsys" |grep -v "swap" |grep "${vgdev}/.*[[:space:]]*.*${vgdev}"|sort|tail -n 1)
109   fs=$(echo "$fsys" |grep -v "swap" |grep "${vgdev}/.*[[:space:]]*filesystem"|sort|tail -n 1)
110   fstype=$(echo "$fs" | awk '{print $3}')
111 
112   IFS=',' array=($vgnp) ; unset IFS
113   len=${#array[@]}
114   pdevlast=${array[@]: -1}
115   pdev1st=${array[0]}
116   # a bug for virt-filesystems
117   if [[ "$pdevlast" == "$pdev1st" && $len -gt 0 ]] ; then
118     base=${pdev1st%%[[:digit:]]*}
119     idx=${pdev1st##${base}}
120     idx=$(($len -1 + $idx))
121     # echo $pdev1st $base $idx
122     pdevlast=${base}${idx}
123   fi
124   # echo $vgdev: $vgnp: $lv: $fs: $fstype, $len, $pdevlast $pdev1st
125   echo "${lv%% *}, $fstype, $pdev1st, $pdevlast"
126 }
127 
128 
129 # resize_dom_lvm domain script_file size_G  # only test on centos7.9
130 function resize_dom_lvm(){
131   # $1 domain name $2 size $3 disk $4 volume  # $5 resize script
132   check_resize_dom_lvm_1st_arg $1
133   ret=$?
134   [[ $ret -ne 0 ]] && return $ret
135 
136   [[ ! -f "$1" && "running" == "$(virsh domstate $1)" ]] && echo "The vm in running, exit" && ret=1
137   [[ $ret -ne 0 ]] && return $ret
138 
139   [[ -f "$1" ]] && img=$1 || img=$(get_dom_1st_disk $1)
140 
141   check_resize_dom_lvm_2st_arg $img $2
142   ret=$?
143   [[ $ret -ne 0 ]] && return $ret
144   size=${2:-20G}
145   img_size=$(qemu-img info --output json $img | grep "virtual-size" | awk -F"[,:]" '{print $2}')
146   osize=$(($img_size/1024/1024/1024))
147   nsize=${size%%[^0-9]*}
148   [[ "$(($osize + 1))" -gt "$nsize"  ]] && echo "The original image size is ${osize}G. Please imput a bigger size." && return 1
149 
150   lvinfo=$(parser_disk_lv_info $1)
151   IFS=', ' array=($lvinfo); unset IFS
152   lvroot=${4:-${array[0]}}
153   fstype=${array[1]}
154   pv_disk=${3:-${array[2]}}
155   [[ "xfs" == "$fstype" ]] && fs_cmd=xfs_growfs || fs_cmd=resize2fs
156   script=$(gen_resize_lvm_script $fs_cmd $lvroot)
157   # virt-sysprep can also works
158   # [[ -f "$1" ]] && virt-customize -a $1 --firstboot $script || virt-customize -d $1 --firstboot $script
159 
160   tmp_img=${img%/*}/temporary.${img#*.}
161   echo "qemu-img create -f qcow2 -o preallocation=metadata $tmp_img $size"
162   echo "Generate a intermediate images: $tmp_img"
163   qemu-img create -f qcow2 -o preallocation=metadata $tmp_img $size
164 
165   virt-filesystems --long -h --all -a $img
166   # https://blog.csdn.net/tutucute0000/article/details/38414449
167   echo "virt-resize --expand $pv_disk --LV-expand $lvroot $img $tmp_img"
168   virt-resize --expand $pv_disk --LV-expand $lvroot "$img" "$tmp_img"
169   echo "virt-customize -a $tmp_img --run $script"
170   virt-customize -a $tmp_img --run $script
171   virt-filesystems --long -h --all -a $tmp_img
172   if [[ $ret -eq 0 ]] ; then
173     echo "mv $tmp_img $img -f"
174     mv $tmp_img $img -f
175   else
176     echo "NOTE: Please double chech and run the follow command manually!"
177     echo "  mv $tmp_img $img -f"
178   fi
179 }
View Code
  1 function gen_resize_lvm_script(){
  2 FILE=/tmp/vir_domain/resize_lvm.sh
  3 cat > $FILE << EOF
  4 # vgdisplay
  5 lvextend -l +100%FREE ${2:-/dev/mapper/centos-root} >> /root/boot1.log
  6 ${1:-xfs_growfs} ${2:-/dev/mapper/centos-root} >> /root/boot1.log
  7 # df -h
  8 EOF
  9 echo "$FILE"
 10 }
 11 
 12 # get_dom_1st_disk domain_name
 13 function get_dom_1st_disk(){
 14     # $1 domain name
 15     FIRST_DISK_EXP='//disk[1][@device="disk"]/source/@file'
 16     tmpxml=/tmp/vir_domain/tmp.xml
 17     mkdir -p /tmp/vir_domain
 18     virsh dumpxml $1 > $tmpxml
 19     img=$(xmllint --xpath $FIRST_DISK_EXP $tmpxml | awk -F'[="]' '!/>/{print $(NF-1)}')
 20     echo "$img"
 21 }
 22 
 23 
 24 function check_resize_dom_lvm_1st_arg(){
 25   if [ -z "$1" ]; then
 26     virsh list --all
 27     echo "usage:"
 28     echo "  resize_dom_lvm domain|file size_G disk volume script_file "
 29     return 1
 30   fi
 31 
 32   if [[ ! -f "$1" ]] ; then
 33      virsh dominfo $1
 34      ret=$?
 35      [[ $ret -ne 0 ]] && return $ret
 36   fi
 37 }
 38 
 39 
 40 function check_resize_dom_lvm_2st_arg(){
 41   if [ -z "$2" ]; then
 42     echo "Error: missing the size parameters($2): "
 43     virt-filesystems --long -h --all -a $1
 44     echo "Get the resize disk($3) and lvm($4) from above information"
 45     echo "And generate resize script by this command:"
 46     echo "  gen_resize_lvm_script"
 47     echo "Check the script is right"
 48     return 1
 49   fi
 50 }
 51 
 52 function parser_disk_info(){
 53   # $1 image
 54   img=$1
 55   # 0 last partition, 1 last pv 2. last vg 3. last vg parent 4. root lv, 5. file type 6. is lvm (check pv)
 56   ret=()
 57   fsys=$(virt-filesystems --long -h --all -a $img)
 58 
 59   # ---------------------- 0 last partition -----------------------
 60   partn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9 ]*partition"|sort|tail -n 1)
 61   # partndev=$(echo "$partn" | awk '{print $1}')
 62   partndev=${partn%% *}
 63   ret+=("$partndev")
 64   # ---------------------- 1 last pv        -----------------------
 65   pvn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9 ]*pv"|sort|tail -n 1)
 66   pvndev=$(echo "$pvn" | awk '{print $1}')
 67   # disk=${3:-/dev/sda2}
 68   ret+=("$pvndev")
 69   # pvndev=${3:-$pvndev}
 70 
 71   # ---------------------- 2 last vg        -----------------------
 72   vgn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9]*[[:space:]]*vg.*"|sort|tail -n 1)
 73   vgdev=$(echo "$vgn" | awk '{print $1}')
 74   ret+=("$vgdev")
 75 
 76   # ---------------------- 3 last vg parent -----------------------
 77   vgnp=${vgn##* }
 78   ret+=("$vgnp")
 79 
 80   # ---------------------- 4 root lv    -----------------------
 81   lv=$(echo "$fsys" |grep "${vgdev}/root[[:space:]]*.*${vgdev}"|sort|tail -n 1)
 82   lvroot=$(echo "$lv" | awk '{print $1}')
 83   # lvroot=${4:-$lvroot}
 84   ret+=("$lvroot")
 85 
 86   # ---------------------- 5 file type -----------------------
 87   fs=$(echo "$fsys" |grep "${lvroot}[[:space:]]*filesystem")
 88   fstype=$(echo "$fs" | awk '{print $3}')
 89   ret+=("$fstype")
 90 
 91   # ---------------------- 6 is lvm   -----------------------
 92   declare -p ret
 93   # echo $pvn, $vgn, $fs, $vgdev
 94   echo $pvndev, $lvroot, $fstype
 95 }
 96 
 97 
 98 
 99 function parser_disk_lv_info(){
100   # $1 image
101   img=$1
102   # 0 last partition, 1 last pv 2. last vg 3. last vg parent 4. root vg, 5. file type 6. is lvm (check pv)
103   ret=()
104   fsys=$(virt-filesystems --long -h --all -a $img)
105   vgn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9]*[[:space:]]*vg[[:space:]]*.*/dev/[A-Za-z1-9]*"|sort|tail -n 1)
106   vgdev=${vgn%% *}
107   vgnp=${vgn##* }
108   lv=$(echo "$fsys" |grep -v "swap" |grep "${vgdev}/.*[[:space:]]*.*${vgdev}"|sort|tail -n 1)
109   fs=$(echo "$fsys" |grep -v "swap" |grep "${vgdev}/.*[[:space:]]*filesystem"|sort|tail -n 1)
110   fstype=$(echo "$fs" | awk '{print $3}')
111 
112   IFS=',' array=($vgnp) ; unset IFS
113   len=${#array[@]}
114   pdevlast=${array[@]: -1}
115   pdev1st=${array[0]}
116   # a bug for virt-filesystems
117   if [[ "$pdevlast" == "$pdev1st" && $len -gt 0 ]] ; then
118     base=${pdev1st%%[[:digit:]]*}
119     idx=${pdev1st##${base}}
120     idx=$(($len -1 + $idx))
121     # echo $pdev1st $base $idx
122     pdevlast=${base}${idx}
123   fi
124   # echo $vgdev: $vgnp: $lv: $fs: $fstype, $len, $pdevlast $pdev1st
125   echo "${lv%% *}, $fstype, $pdev1st, $pdevlast"
126 }
127 
128 
129 # resize_dom_lvm domain script_file size_G  # only test on centos7.9
130 function resize_dom_lvm(){
131   # $1 domain name $2 size $3 disk $4 volume  # $5 resize script
132   check_resize_dom_lvm_1st_arg $1
133   ret=$?
134   [[ $ret -ne 0 ]] && return $ret
135 
136   [[ ! -f "$1" && "running" == "$(virsh domstate $1)" ]] && echo "The vm in running, exit" && ret=1
137   [[ $ret -ne 0 ]] && return $ret
138 
139   [[ -f "$1" ]] && img=$1 || img=$(get_dom_1st_disk $1)
140 
141   check_resize_dom_lvm_2st_arg $img $2
142   ret=$?
143   [[ $ret -ne 0 ]] && return $ret
144   size=${2:-20G}
145   img_size=$(qemu-img info --output json $img | grep "virtual-size" | awk -F"[,:]" '{print $2}')
146   osize=$(($img_size/1024/1024/1024))
147   nsize=${size%%[^0-9]*}
148   [[ "$(($osize + 1))" -gt "$nsize"  ]] && echo "The original image size is ${osize}G. Please imput a bigger size." && return 1
149 
150   lvinfo=$(parser_disk_lv_info $1)
151   IFS=', ' array=($lvinfo); unset IFS
152   lvroot=${4:-${array[0]}}
153   fstype=${array[1]}
154   pv_disk=${3:-${array[2]}}
155   [[ "xfs" == "$fstype" ]] && fs_cmd=xfs_growfs || fs_cmd=resize2fs
156   script=$(gen_resize_lvm_script $fs_cmd $lvroot)
157   # virt-sysprep can also works
158   # [[ -f "$1" ]] && virt-customize -a $1 --firstboot $script || virt-customize -d $1 --firstboot $script
159 
160   tmp_img=${img%/*}/temporary.${img#*.}
161   echo "qemu-img create -f qcow2 -o preallocation=metadata $tmp_img $size"
162   echo "Generate a intermediate images: $tmp_img"
163   qemu-img create -f qcow2 -o preallocation=metadata $tmp_img $size
164 
165   virt-filesystems --long -h --all -a $img
166   # https://blog.csdn.net/tutucute0000/article/details/38414449
167   echo "virt-resize --expand $pv_disk --LV-expand $lvroot $img $tmp_img"
168   virt-resize --expand $pv_disk --LV-expand $lvroot "$img" "$tmp_img"
169   echo "virt-customize -a $tmp_img --run $script"
170   virt-customize -a $tmp_img --run $script
171   virt-filesystems --long -h --all -a $tmp_img
172   if [[ $ret -eq 0 ]] ; then
173    echo "mv $tmp_img $img -f"
174     mv $tmp_img $img -f
175   else
176     echo "NOTE: Please double chech and run the follow command manually!"
177     echo "  mv $tmp_img $img -f"
178   fi
179 }
View Code

v1.0 

 1 # resize_dom_lvm domain script_file size_G  # only test on centos7.9
 2 function resize_dom_lvm(){
 3   # $1 domain name $2 size $3 disk $4 volume  # $5 resize script
 4   if [ -z "$1" ]; then
 5     virsh list --all
 6     echo "usage:"
 7     echo "  resize_dom_disk domain|file size_G disk volume script_file "
 8     return 1
 9   fi
10 
11   if [ -f "$1" ]; then
12     img=$1
13   else
14     [[ "running" == "$(virsh domstate $1)" ]] && echo "The vm in running, exit" && return 1 
15     FIRST_DISK_EXP='//disk[1][@device="disk"]/source/@file'
16     tmpxml=/tmp/vir_domain/tmp.xml
17     mkdir -p /tmp/vir_domain
18     virsh dumpxml $1 > $tmpxml
19     img=$(xmllint --xpath $FIRST_DISK_EXP $tmpxml | awk -F'[="]' '!/>/{print $(NF-1)}') 
20   fi
21 
22   if [ -z "$2" ]; then
23     echo "Error: missing the size parameters($2): "
24     virt-filesystems --long -h --all -a $img
25     echo "generate resize script by this command:"
26     echo "  gen_resize_lvm_script"
27     echo "and check the script by the above infromation"
28     return 1
29   fi
30 
31   echo $img
32   fsys=$(virt-filesystems --long -h --all -a $img)
33   pvn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9 ]*pv"|sort|tail -n 1)
34   pvndev=$(echo "$pvn" | awk '{print $1}')
35   # disk=${3:-/dev/sda2}
36   pvndev=${3:-$pvndev}
37 
38   vgn=$(echo "$fsys" |grep -o "/dev/[A-Za-z1-9]*[[:space:]]*vg"|sort|tail -n 1)
39   vgdev=$(echo "$vgn" | awk '{print $1}')
40 
41   lv=$(echo "$fsys" |grep "${vgdev}/root[[:space:]]*.*${vgdev}"|sort|tail -n 1)
42   lvroot=$(echo "$lv" | awk '{print $1}')
43   lvroot=${4:-$lvroot}
44 
45   fs=$(echo "$fsys" |grep "${lvroot}[[:space:]]*filesystem")
46   fstype=$(echo "$fs" | awk '{print $3}')
47 
48   echo $pvn, $vgn, $fs, $vgdev
49   echo $pvndev, $lvroot, $fstype
50 
51 
52   # virt-sysprep can also works
53   [[ "xfs" == "$fstype" ]] && fs_cmd=xfs_growfs || fs_cmd=resize2fs 
54   script=$(gen_resize_lvm_script $fs_cmd $lvroot)
55   # [[ -f "$1" ]] && virt-customize -a $1 --firstboot $script || virt-customize -d $1 --firstboot $script
56 
57 
58   tmp_img=${img%/*}/temporary.${img#*.}
59   rm "$tmp_img" -f
60   mv $img $tmp_img -f 
61   rm "$img" -f
62  
63   size=${2:-20G}
64   echo "qemu-img create -f qcow2 -o preallocation=metadata $img $size"
65   qemu-img create -f qcow2 -o preallocation=metadata $img $size
66 
67   virt-filesystems --long -h --all -a $tmp_img
68 
69   lv_disk=${lvroot:-/dev/centos/root}
70   # https://blog.csdn.net/tutucute0000/article/details/38414449
71   echo "virt-resize --expand $pvndev --LV-expand $lv_disk $tmp_img $img"
72   virt-resize --expand $pvndev --LV-expand $lv_disk "$tmp_img" "$img"
73   [[ -f "$1" ]] && virt-customize -a $1 --run $script || virt-customize -d $1 --run $script 
74   virt-filesystems --long -h --all -a $img
75 }

Inject ssh key   

function inject_ssh_key(){
# $1 domain, $2 USER $3 Key path 
USER=${2:-$USER}
USER=${USER:-root}
KEY=$(realpath ~/.ssh/id_rsa.pub)
# echo $KEY
FILENAME=${3:-$KEY}
[[ "$USER" == "root" ]] && KEYPATH="/root/.ssh/" || KEYPATH="/home/$USER/.ssh/"   
# These 2 commands does not works
# https://bugzilla.redhat.com/show_bug.cgi?id=1378311
virt-customize -d $1 --ssh-inject $USER:file:$FILENAME --selinux-relabel
# virt-sysprep -d $1 --ssh-inject $USER:file:$FILENAME
# virt-copy-in -d $1 $KEY $KEYPATH
}
 
原文地址:https://www.cnblogs.com/shaohef/p/14799860.html