KVM#KVM自动化管理系统

KVM虚拟机自动化管理系统脚本

#!/bin/bash
# Author:kakaops
# Email:17852032278@139.com

source kvm-func.sh
clear
menu
while true
do  
    clear && menu
    read -p ">>:" keyword
    clear && menu
    case $keyword in
        1|a)
            vmall
            ;;
        2|b)
            baseos
            ;;
        3|c)
            copyvm
            ;;
        4|d)
            diskadd
            ;;
        5|e)
            memadd
            ;;
        6|f)
            clone 
            ;;
        7|g)
            guazai 
            ;;
        8|h)
            juan 
            ;;
        9|i)
            snapshot 
            ;;
       10|g)
            net_bri
            ;;
        q|Q)
            echo -e "\e[1;34m退出=============\e[0m"
            exit
            ;;
        *)
            echo -e "\e[1;35m输入无效,请重新输入\e[0m"
            ;;
    esac
done

KVM虚拟机自动化管理系统函数库

#!/bin/bash
# Author:kakaops
# Email:17852032278@139.com

# 一级菜单
function menu(){
cat<<!
                         欢迎使用kvm虚拟机管理系统
===================================================================================
  1|a) 查看正在运行的虚拟机和全部虚拟机        2|b) 虚拟机基本管理命令
  3|c) 快速创建一个虚拟机(请先确保已         4|d) 给指定的虚拟机添加指定容量的硬盘
       经在kvm中安装了一个虚拟机模板)      
  5|e) 给指定的虚拟机添加制定容量的mem内存    6|f) 虚拟机的快速克隆
  7|g) 虚拟机挂载到主机              8|h) 虚拟机创建存储卷 
  9|i) 创建虚拟机快照i                     10|g) 配置桥接网卡
  q|Q) 退出kvm虚拟机管理系统
===================================================================================
!
}

# 查看正在运行的虚拟机和全部虚拟机
function vmall(){
echo -e "\e[32;1m正在运行的虚拟机\e[0m"
echo "$(virsh list)"
echo -e "\e[34;1m全部的虚拟机\e[0m"
echo "$(virsh list --all)"
echo "10s后自动返回到主菜单"
sleep 10
}

# 请选择虚拟机基本管理命令
function menu2(){
cat<<!
=================================================================
 1、启动虚拟机       2、暂停虚拟机      3、恢复虚拟机
 4、关闭虚拟机      5、强制断电关机     6、重启虚拟机      
 7、强制断电重启     8、删除虚拟机      9、设置开机自启
 10、关闭开机自启      11、查看运行虚拟机ip   12 、查看域磁盘信息
  13、删除域磁盘      Q|q、退出基本命令模块 
                           连续输入两次q,返回主菜单
=================================================================
!
}

# 通过循环读取文件。可以实现echo多行加颜色
function readline(){
while read line
do
    echo -e "\e[31;1m${line}\e[0m"
done < a.txt
rm -rf a.txt
}

function baseos(){
while true
do
    menu2
    echo "您可以进行的操作的虚拟机:"
    echo "$(virsh list --all)"
    read -p "输入要操作的虚拟机名>>:" vmname
    read -p "输入要进行的操作>>:" number
    case $number in
        1)
            virsh start "${vmname}" &> a.txt
            readline
            ;;
        2)
            virsh suspend "${vmname}" &> a.txt
            readline
            ;;
        3)
            virsh resume "${vmname}" &>a.txt
            readline
            ;;
        4)
            virsh shutdown "${vmname}" &>a.txt
            readline
            sleep 10
            ;;
        5)
            virsh destroy "${vmname}" &>a.txt
            readline
            ;;
        6)
            virsh reboot "${vmname}" &>a.txt
            readline
            ;;
        7)
            virsh reset "${vmname}" &>a.txt
            readline
            sleep 10
            ;;
        8)
            virsh undefine "${vmname}" &>a.txt
            readline
            ;;
        9)
            virsh autostart "${vmname}" &>a.txt
            readline
            ;;
        10)
            virsh autostart --disable "${vmname}" &>a.txt
            readline
            ;;
        11)
            virsh domifaddr "${vmname}" &>a.txt
            readline
            ;;
        12)
            virsh domblklist "${vmname}" &>a.txt
            readline
            ;;
        13)
            virsh domblklist "${vmname}"
            while true
            do
                read -p "输入要删除的磁盘名>>:" diskdel
                virsh domblklist sunlizhen |awk '{print $1}' >a.txt
                grep -w "${diskdel}" a.txt &>/dev/null
                if [ $? -eq 0 ];then
                    rm -rf a.txt
                    break
                else
                    echo "磁盘不存在,请重新输入"
                fi
            done
            virsh detach-disk "${vmname}" "${diskdel}" --persistent
            virsh domblklist "${vmname}"
            echo -e "\e[33;1m磁盘"${diskdel}"已经成功从域"${vmname}"删除\e[0m"
            ;;
        q|Q)
            clear && menu
            break
            ;;
        *)
            echo -e "\e[31;1m输入无效,请重新输入\e[0m"
            ;;
    esac
done
}



# 镜像模板快速创建虚拟机,自动生成uuid和mac地址,用强大的sed修改配置文件
function copyvm(){
    echo "您现有的虚拟机模板为:"
    echo "$(virsh list --all)"
    virsh list --all |awk -F'[ ]*' '{print $3}' >b.txt
    while true
    do 
        read -p "请输入模板虚拟机名称>>:" old
        read -p "请输入要创建的虚拟机名称>>:" new
        grep -w "${old}" b.txt &>/dev/null
        if [ $? -eq 0 ] && [ "$old" ] && [ "$new" ];then
            rm -rf b.txt
            break
        else
            echo "请输入已经存在的虚拟机模板,且输入不能为空,请重新输入" 
        fi
    done
    echo "正在进行镜像模板复制"
    cp /etc/libvirt/qemu/"${old}".xml /etc/libvirt/qemu/"${new}".xml
    cp /var/lib/libvirt/images/"${old}".qcow2 /var/lib/libvirt/images/"${new}".qcow2 && \
    echo "镜像模板复制成功,正在修改新虚拟机的配置文件"
    sleep 1
    cd /etc/libvirt/qemu
    sed -ri "s#<name>"$old"</name>#<name>$new</name>#" "${new}".xml
    sed -ri "s#<uuid>[[:alnum:]-]*</uuid>#<uuid>$(uuidgen)</uuid>#" "${new}".xml
    sed -ri "s#(<source file='/var/lib/libvirt/images/)"${old}"(.qcow2')#\1"${new}"\2#" "${new}".xml
    n=$(echo -n 52:54:00; dd bs=1 count=3 if=/dev/random 2>/dev/null | hexdump -v -e '/1 ":%02X"')
    sed -ri "s#<mac address='52:54:00:[[:alnum:]:]*'/>#<mac address='"$n"'/>#" "${new}".xml
    virsh define "${new}".xml && systemctl restart libvirtd && \
    echo "已经成功创建新虚拟机${new}" && virsh list --all
    grep "net.ipv4.ip_forward = 1/" /etc/sysctl.conf
    if [ $? -eq 0 ];then
       pass
    else
       sed -ri '$ a net.ipv4.ip_forward = 1' /etc/sysctl.conf
    fi
}


# 动态给指定虚拟机添加指定容量的磁盘,创建磁盘模板,筛选未定义的磁盘名
function diskadd(){
echo "请选择已经存在的虚拟机添加磁盘"
echo "$(virsh list --all)"
read -p "请输入需要添加磁盘的虚拟机名称>>:" vm_name
read -p "请输入需要添加磁盘的容量,例如2G>>:" disk_size
virsh list --all |awk -F'[ ]*' '{print $3}' >b.txt
while true
do
    grep -w "${vm_name}" b.txt &>/dev/null
    if [ $? -eq 0 ] && [ "${vm_name}" ];then
        break
    else
        echo "输入的虚拟机不存在,请重新输入"
        read -p "请输入需要添加磁盘的虚拟机名称>>:" vm_name
    fi
done
while true
do
    if [[ ! "${disk_size}" =~ G$ ]] || [[ ! "${disk_size::-1}" =~ ^[0-9]+$ ]];then
        echo "输入的容量格式不符合要求,请重新输入,例如2G"
        read -p "请输入需要添加磁盘的容量,例如2G>>:" disk_size
    else
        break
    fi
done
cd /var/lib/libvirt/images/
mkdir "${vm_name}".disk &>/dev/null
qemu-img create -f qcow2 "${vm_name}".disk/"${vm_name}".qcow2 "${disk_size}" >/dev/null
if [ -e disk_mod.xml ];then
    rm -rf disk_mod.xml
fi
cat >>disk_mod.xml<<!
echo "<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2' cache='writeback' io='threads'/>
  <source file='"\${vdisk_path}"'/>
  <target dev='"\${device_name}"' bus='virtio'/>
</disk>"
!
#cat disk_mod.xml
virsh domblklist "${vm_name}" |awk -F "(vd)" '/\// {print $2}'|awk '{print $1}' >read.txt
declare -A disk_arr
while read line
do
    disk_arr[$line]=1
done < read.txt
for i in {a..z}
do
    w=${disk_arr[$i]}
    echo $w
    if [ -z "$w" ];then
        disk_name=vd$i
        echo -e "\e[33;1m添加的磁盘名称是$disk_name\e[0m"
        break
    fi
done
vdisk_path=/var/lib/libvirt/images/"${vm_name}".disk/"${vm_name}".qcow2
device_name="${disk_name}"
#cat disk_mod.xml
echo "${vdisk_path}"
echo "${device_name}"
source disk_mod.xml > disk.xml
cat disk.xml
virsh attach-device "$vm_name" disk.xml --persistent
virsh domblklist "${vm_name}"
echo -e "\e[32;1m已经成功为虚拟机"${vm_name}"添加大小为"${disk_size}"的磁盘"${device_name}"\e[0m"
echo -e "\e[32;1m"${vm_name}"的所有新添加的磁盘为您存放在"${vdisk_path}"\e[0m"
rm -rf b.txt
rm -rf read.txt
rm -rf disk_mod.xml
rm -rf disk.xml
sleep 8
}


function memadd(){
echo "请选择已经存在的虚拟机添加内存mem"
echo "$(virsh list --all)"
read -p "请输入需要添加内存mem的虚拟机名称>>:" vm_name
virsh list --all |awk -F'[ ]*' '{print $3}' >b.txt
while true
do
    grep -w "${vm_name}" b.txt &>/dev/null
    if [ $? -eq 0 ] && [ "${vm_name}" ];then
        rm -rf b.txt
        break
    else
        echo "您输入的虚拟机不存在,请重新输入"
        read -p "请输入需要添加内存mem的虚拟机名称>>:" vm_name
    fi
done
read -p "    是否为虚拟机"${vm_name}"重新设定最大内存(Y|N),如果您选择同意Y,将会关闭此虚拟机,设置内存完成后,将会为您重新开启此虚拟机,请慎重修改此虚拟机最大内存,如果分配内存过大,将会导致此虚拟机无法开机,如果您选择N,不同意关闭此虚拟机,会为您在线添加内存mem>>:" chose
while true
do
    case $chose in
        Y)
            read -p "请输入需要添加内存mem的容量,例如2G>>:" disk_size
            while true
            do
                if [[ ! "${disk_size}" =~ G$  ]] || [[ ! "${disk_size::-1}" =~ ^[0-9]+$  ]];then
                echo "输入的容量格式不符合要求,请重新输入,例如2G"
                read -p "请输入需要添加磁盘的容量,例如2G>>:" disk_size
                else
                    break
                fi
            done
            virsh destroy "${vm_name}" &>/dev/null
            virsh setmaxmem "${vm_name}" "${disk_size}" --config
            virsh start "${vm_name}" >a.txt
            readline
            break
            ;;
        N)
            read -p "请输入需要添加内存mem的容量,例如2G>>:" disk_size
            while true
            do
                if [[ ! "${disk_size}" =~ G$   ]] || [[ ! "${disk_size::-1}" =~ ^[0-9]+$   ]];then
                echo "输入的容量格式不符合要求,请重新输入,例如2G"
                read -p "请输入需要添加磁盘的容量,例如2G>>:" disk_size
                else
                    break
                fi
            done
            virsh setmem sunlizhen "$disk_size"
            break
            ;;
        *)
            echo "输入无效,请重新输入"
            read -p "是否为"${vm_name}"重新设定最大内存(Y|N),如果您选择同意Y,将会关闭虚拟机"${vm_name}">>:" chose
    esac
done
echo -e  "\e[32;1m域"${vm_name}"的当前信息\e[0m"
virsh dominfo "${vm_name}"
}


#进行虚拟机的快速克隆
function clone(){
cat<<!
请选择克隆模式
1、快速克隆
2、自定义克隆
q、退出
!
while true
do
    read -p ">>:" modd
    case $modd in
        1)
            intname
            startlist
            virsh destroy "${vm_name}" &>/dev/null
            virt-clone -o "${vm_name}" --auto-clone
            if [ "$a" == 1 ];then
                virsh start "${vm_name}" &>/dev/null
            fi
            break
            ;;
        2)
            intname
            read -p "请输入新的虚拟机名称>>:" newname
            while true
            do
                if [ "$newname" ];then
                    read -p "请输入要用的磁盘镜像绝对路径,输入为空则用模板机的镜像卷,输入错误的路径会创建失败>>:" newfile
                    if [ "$newfile" ];then
                        startlist
                        virsh destroy "${vm_name}" &>/dev/null
                        virt-clone -o "${vm_name}" -n "${newname}" -f "${newfile}"
                        if [ "$a" == 1 ];then
                            virsh start "${vm_name}" &>/dev/null
                        fi
                        break
                    else
                        startlist
                        virsh destroy "${vm_name}" &>/dev/null
                        virt-clone -o "${vm_name}" -n "${newname}" --auto-clone
                        if [ "$a" == 1 ];then
                            virsh start "${vm_name}" &>/dev/null
                        fi
                        break
                    fi
                fi
            done
            break
            ;;
        q)
            exit
            ;;
        *)
            echo "输入无效,请重新输入"
            ;;
    esac
done
virsh list --all >a.txt
readline
}

function intname(){
echo "您可以选择的虚拟机模板有,正在运行的域不能进行克隆,如果您选择了正在运行的域,那么将会为您执行关机命令,克隆结束后会恢复其运行状态:"
virsh list --all
read -p "请输入需要进行克隆的模板机的虚拟机名称>>:" vm_name
virsh list --all |awk -F'[ ]*' '{print $3}' >b.txt
while true
do
    grep -w "${vm_name}" b.txt &>/dev/null
    if [ $? -eq 0 ] && [ "${vm_name}" ];then
        rm -rf b.txt
        break
    else
        echo "您输入的虚拟机不存在,请重新输入"
        read -p "请输入需要进行克隆的模板机的虚拟机名称>>:" vm_name
    fi
done
}
function startlist(){
virsh list |awk '{print $2}' >a.txt
grep -w "${vm_name}" a.txt &>/dev/null
if [ $? -eq 0 ];then
    a=1
fi
}


# 挂载指定虚拟机到真机指定目录
function guazai(){
virsh list --all
read -p "请输入需要进行挂载的虚拟机名称,输入需要进行克隆的模板机的虚拟机名称,如果模板机处于运行状态会为您进行关机,挂载成功后会为您开启模板虚拟
机>>:" vm_name
virsh list --all |awk -F'[ ]*' '{print $3}' >b.txt
while true
do
    grep -w "${vm_name}" b.txt &>/dev/null
    if [ $? -eq 0 ] && [ "${vm_name}" ];then
        rm -rf b.txt
        break
    else
        echo "您输入的虚拟机不存在,请重新输入"
        read -p "请输入需要进行克隆的模板机的虚拟机名称,如果模板机处于运行状态会为您进行关机,挂载成功后会为您开启模板虚拟机>>:" vm_name
    fi
done
while true
do
    read -p "请输入宿主机被挂载目录绝对路径,保证路径格式正确,建议输入一个未创建的空目录路径(例如:如果您输入错误格式的目录路径/mnt///gua/,系统仍然会为你创建目录/mnt/gua/,但是会造成磁盘挂载失败)>>:" dir
    ls "$dir" &>/dev/null
    if [ $? -eq 0 ];then
        break
    else
        n=${dir:0:1}
        if [[ "$n" =~ "/" ]];then
            mkdir -p "$dir"
            break
        else
            echo "您输入的格式不正确,请重新输入"
        fi
    fi
done
startlist
virsh destroy "${vm_name}" >/dev/null
guestmount -d "${vm_name}" -m /dev/centos/root --rw "${dir}"
if [ "$a" == 1 ];then
    virsh start "${vm_name}" >/dev/null
fi
echo -e "\e[34;1m虚拟机挂载成功\e[0m"
ls "${dir}"
sleep 5
}

# 构建kvm存储池
function juan(){
function poolcreate(){
while true
do
while true
do
    read -p "输要构建的存储池的名称>>:" poolname
    if [ "$poolname" ];then
        break
    else
        echo "输入不能为空"
        continue
    fi
done
while true
do
    read -p "请输入用于构建存储池的目录路径>>:" dir
    ls "$dir" &>/dev/null
    if [ $? -eq 0 ];then
        break
    else
        n=${dir:0:1}
        if [[ "$n" =~ "/" ]];then
            mkdir -p "$dir"
            break
        else
            echo "您输入的格式不正确,请重新输入"
        fi
    fi
done
echo -e "\e[35;1m正在为您定义和构建存储池\e[0m" &&\
virsh pool-define-as "$poolname" --type dir --target "$dir" &&\
virsh pool-build "$poolname"
if [ $? -eq 0 ];then
    virsh pool-list --all
    echo -e "\e[35;1m存储池构建成功,您可以接着对存储池进行其他操作\e[0m"
    break
else
    continue
fi
done
}
poolcreate
function poollist(){
cat<< !
1) 激活已经定义的存储池
2) 为存储池设置开机自启
3) 取消存储池的开机自启
4) 在存储池中创建qcow2格式的虚拟镜像卷
5) 在存储池中删除虚拟镜像卷
6) 取消激活存储池
7) 删除存储池目录
8) 取消定义存储池
9) 返回主菜单
!
}
function dowhat(){
while true
do
echo "请输入您要进行操作的存储池名称"
read -p ">>:" vmname
virsh pool-list --all |awk '{print $1}' >b.txt
grep -w "$vmname" b.txt &>/dev/null
if [ $? -eq 0 ] && [ "$vmname" ];then
    rm -rf b.txt
    break
else
    echo ”输入的存储池不存在,或者不符合要求“
    continue
fi
done
}
function juan(){
while true
do
echo "请输入虚拟卷名称,不用写扩展名"
read -p ">>:" juanname
if [ $juanname ];then
    break
else
    echo "输入错误,请重新输入"
    break
fi
done
}
function size(){
while true
do
    echo "输入要创建的虚拟卷的大小,比如2G:"
    read -p ">>:" disk_size
    if [[ ! "${disk_size}" =~ G$ ]] || [[ ! "${disk_size::-1}" =~ ^[0-9]+$ ]];then
        echo "输入错误,请重新输入"
        continue
    else
        break
    fi
done
}
function diroo(){
while true
do
    read -p "请输入要删除的存储池目录路径>>:" dir
    ls "$dir" &>/dev/null
    if [ $? -eq 0 ];then
        break
    else
        n=${dir:0:1}
        if [[ "$n" =~ "/" ]];then
            mkdir -p "$dir"
            break
        else
            echo "您输入的格式不正确,请重新输入"
        fi
    fi
done
}
function koko(){
while true
do
   virsh pool-list --all
   poollist
   dowhat
   echo "请输入要进行的操作代号"
   read -p ">>:" num
   case $num in
       1)
           virsh pool-start "$vmname"
           ;;
       2)
           virsh pool-autostart "$vmname"
           ;;
       3)
           virsh pool-autostart --disable "$vmname"
           ;;
       4)
           juan
           size
           virsh vol-create-as "$vmname" "${juanname}".qcow2 "$disk_size" --format qcow2
           ;;
       5)
           juan
           virsh vol-delete --pool "$vmname" "${juanname}".qcow2
           ;;
       6)
           virsh pool-destroy "$vmname"
           ;;
       7)
           diroo
           virsh pool-delete "$dir"
           ;;
       8)
           virsh pool-undefine "$vmname"
           ;;
       9)
           break
           ;;
      *)
           echo "输入错误,请重新输入"
           ;;
   esac
done
}
function cunchujuan(){
while true
do
   cat<<!
    y) 继续对存储池进行其他操作
    n) 不进行操作,继续进行存储池构建
    q) 返回主菜单
!
    read -p ">>:" num
    case $num in
        y)
            koko
            ;;
        n)
            poolcreate
            ;;
        q)
            break
            ;;
        *)
            echo "输入无效,请重新输入"
  esac
done
}
cunchujuan
}

# 创建虚拟机快照
function snapmenu(){
cat<<!
1)、创建快照
2)、查看快照
3)、恢复快照
4)、删除快照
5)、回主菜单
!
}
function snapshot_create(){
while true
do
    virsh list --all
    virsh list --all |awk -F'[ ]*' '{print $3}' >b.txt
    read -p "输入虚拟机名称>>:" vm_name
    grep -w "${vm_name}" b.txt &>/dev/null
    if [ $? -eq 0 ] && [ "${vm_name}" ];then
        rm -rf b.txt
        break
    else
        echo "您输入的虚拟机不存在,请重新输入"
        continue
    fi
done
}
function no_name(){
while true
do
virsh snapshot-list "$vm_name"
virsh snapshot-list "$vm_name" >b.txt
read -p "输入快照名称>>:" snapname
grep -w "$snapname" b.txt
if [ $? -ne 0 ] && [ "$snapname" ];then
    rm -rf b.txt
    break
else
    echo "输入错误,请重新输入"
    continue
fi
done
}
function yes_name(){
while true
do
virsh snapshot-list "$vm_name"
virsh snapshot-list "$vm_name" >b.txt
read -p "输入快照名称>>:" snapname
grep -w "$snapname" b.txt
if [ $? -eq 0 ] && [ "$snapname" ];then
    rm -rf b.txt
    break
else
    echo "输入错误,请重新输入"
    continue
fi
done
}
function snapshot(){
while true
do
snapmenu
read -p ">>:" num
case $num in
    1)
        snapshot_create
        no_name
        virsh snapshot-create-as "${vm_name}" "$snapname"
        virsh snapshot-list "$vm_name"
        ;;
    2)
        snapshot_create
        virsh snapshot-list "$vm_name"
        ;;
    3)
        snapshot_create
        yes_name
        virsh shutdown "$vm_name" &>>/dev/null
        virsh snapshot-revert "$vm_name" "$snapname"
        virsh snapshot-list "$vm_name"
        ;;
    4)
        snapshot_create
        yes_name
        virsh snapshot-delete --snapshotname "$snapname" "$vm_name"
        virsh snapshot-list "$vm_name"
        ;;
    5)
        break
        ;;
    *)
        echo "输入错误,请重新输入"
        ;;
esac
done
}

# 配置一个桥接网络
function add_bri(){
cd /etc/sysconfig/network-scripts/
cat >>bri_mode<<!
echo "TYPE=Bridge
NAME=br0
DEVICE=br0
ONBOOT="yes"
BOOTPROTO=static
IPADDR="\$ip_addr"
GATEWAY="\$gate_addr"
NETMASK=255.255.255.0
DNS1=114.114.114.114
DNS2=8.8.8.8"
!
ip_addr=$(ip a|grep "scope global"|grep "dynamic"|awk -F'[/ ]+' '{print $3}')
gate=$(echo "$ip_addr"|awk -F'.' 'BEGIN{OFS="."} {print $1,$2,$3}')
gate_addr=${gate}.1
source bri_mode > ifcfg-br0
card=$(ip a|grep ^2|awk -F'[ : ]' '{print $3}')
cp ifcfg-${card}{,.back}
cat >>ifcfg-"$card"<<!
NAME=$card
DEVICE=$card
ONBOOT=yes
BRIDGE=br0
!
systemctl restart network
systemctl restart libvirtd
}
function del_bir(){
cd /etc/sysconfig/network-scripts/
rm -rvf ifcfg-br0
mv ifcfg-enp0s25{,.bak}
mv ifcfg-enp0s25{.back,}
systemctl restart libvirtd
systemctl restart network
}
function net_bri(){
while true
do
    cat<<!
    1) 配置桥接网卡
  2) 删除桥接网卡
    q) 回主菜单
!
read -p ">>:" num
case $num in
    1)
        add_bri
        ;;
    2)
        del_bir
        ;;
    q)
        break
        ;;
    *)
        echo "输入无效,请重新输入"
        ;;
esac
done
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值