zoukankan      html  css  js  c++  java
  • 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
    }
     
  • 相关阅读:
    Java并发编程原理与实战二十九:Exchanger
    Java并发编程原理与实战二十八:信号量Semaphore
    Java并发编程原理与实战二十七:循环栅栏:CyclicBarrier
    Java并发编程原理与实战二十六:闭锁 CountDownLatch
    Java并发编程原理与实战二十五:ThreadLocal线程局部变量的使用和原理
    Java并发编程原理与实战二十四:简易数据库连接池
    Java并发编程原理与实战二十三:Condition原理分析
    Java并发编程原理与实战二十二:Condition的使用
    Java并发编程原理与实战二十一:线程通信wait&notify&join
    Java并发编程原理与实战二十:线程安全性问题简单总结
  • 原文地址:https://www.cnblogs.com/shaohef/p/14799860.html
Copyright © 2011-2022 走看看