● 写好了自动化脚本安装虚拟机
1、安装virt
yum install -y qemu-kvm libvirt libvirt-client virt-install virt-viewer bridge-utils
systemctl start libvirtd && systemctl enable libvirtd2、上传iso镜像到/data/iso
上传iso镜像到服务器的 /data/iso/ 目录下3、mvstat脚本
#!/usr/bin/env bash
# 红色
red() {
echo -e $'\033[0;31m'"$1"$'\033[0m'
}
# 黄色
yellow() {
echo -e $'\033[0;33m'"$1"$'\033[0m'
}
# 用法: print_table "ID|名称|状态" <(paste id mvname mvstatus)
print_table() {
local headers="$1"
local data_source="$2"
# 生成id mvname mvstatus
virsh list --all | awk 'NR>2 && NF>0 {print $1}' > /tmp/id
virsh list --all | awk 'NR>2 && NF>0 {print $2}' > /tmp/vmname
virsh list --all | awk 'NR>2 && NF>0 {print $3}' > /tmp/vmstatus
# 计算字符串显示宽度(汉字占2宽,英文/数字占1宽)
str_width() {
local str="$1"
echo -n "$str" | awk '{
width = 0
for (i = 1; i <= length($0); i++) {
char = substr($0, i, 1)
if (char ~ /[^\x00-\x7F]/) {
width += 2
} else {
width += 1
}
}
print width
}'
}
# 左对齐并填充到目标宽度
align_left() {
local str="$1"
local target_width="$2"
local current_width=$(str_width "$str")
local spaces=$((target_width - current_width))
if [ $spaces -lt 0 ]; then spaces=0; fi
printf "%s%*s" "$str" "$spaces" ""
}
# 生成横线
make_line() {
local count=$1
printf '%*s' "$count" '' | tr ' ' '-'
}
# 收集数据
local data=("$headers")
while IFS=$'\t' read -r line; do
if [[ -n "$line" ]]; then
data+=("$(echo "$line" | tr '\t' '|')")
fi
done < <(eval "$data_source" 2>/dev/null)
# 获取列数
IFS='|' read -ra header_fields <<< "$headers"
local col_count=${#header_fields[@]}
# 计算每列最大宽度
declare -a max_width
for ((col=0; col<col_count; col++)); do
max_width[$col]=0
for line in "${data[@]}"; do
IFS='|' read -ra fields <<< "$line"
if [[ $col -lt ${#fields[@]} ]]; then
local width=$(str_width "${fields[$col]}")
[[ $width -gt ${max_width[$col]} ]] && max_width[$col]=$width
fi
done
done
# 打印表格分隔线
print_separator() {
for col in "${!max_width[@]}"; do
printf "+%s" "$(make_line $((max_width[$col] + 2)))"
done
echo "+"
}
# 打印数据行
print_data_row() {
local -a fields=("$@")
for col in "${!max_width[@]}"; do
local field="${fields[$col]:-}"
local pad=$((max_width[$col] - $(str_width "$field")))
printf "| %s%${pad}s " "$field" ""
done
echo "|"
}
# 输出表格
print_separator
for line in "${data[@]}"; do
IFS='|' read -ra fields <<< "$line"
print_data_row "${fields[@]}"
print_separator
done
}
# 关机\开机\重启\删除\添加磁盘\卸载磁盘
DELTE_VM(){
QEMU_DIR="/data/virt-data"
print_table "ID|名称|状态" "paste /tmp/id /tmp/vmname /tmp/vmstatus"
read -r -p "请选择机器名称: " VIRTNAME
if [ -z "${VIRTNAME}" ];then
red "机器名称不能为空!"
exit 1
else
grep -w ${VIRTNAME} /tmp/vmname > /dev/null 2>&1
if [ $? -eq 0 ];then
rm -rf /tmp/id /tmp/vmname /tmp/vmstatus
else
red "您选择的机器不存在!"
exit 1
fi
if [ "${VIRTNAME}" == "kzprd" ];then
yellow "${VIRTNAME} 主机不让删除,确定删除请输入删除密码进行删除."
yellow "密码提示: 123@"
read -r -p "请输入密码: " PASSWORDS
if [ "${PASSWORDS}" == "123@qwe~~" ];then
yellow "选择的机器是: ${VIRTNAME}"
else
red "您输入的密码有误机器不删除,即将退出!"
exit 1
fi
else
yellow "选择的机器是: ${VIRTNAME}"
fi
fi
echo "1 关机"
echo "2 开机"
echo "3 重启"
echo "4 删除"
echo "5 添加磁盘"
echo "6 卸载磁盘"
read -r -p "请选择(1-3): " INPUTID
if [ "${INPUTID}" == "1" ];then
virsh destroy ${VIRTNAME} > /dev/null 2>&1 && yellow "机器${VIRTNAME}已关机" || red "机器关机失败${VIRTNAME}"
elif [ "${INPUTID}" == "2" ];then
virsh start ${VIRTNAME} > /dev/null 2>&1 && yellow "机器${VIRTNAME}已开机" || red "机器开机失败${VIRTNAME}"
elif [ "${INPUTID}" == "3" ];then
virsh reset ${VIRTNAME} > /dev/null 2>&1 && yellow "机器${VIRTNAME}已重启" || red "机器重启失败${VIRTNAME}"
elif [ "${INPUTID}" == "4" ];then
virsh destroy ${VIRTNAME} > /dev/null 2>&1
virsh undefine ${VIRTNAME} > /dev/null 2>&1 && yellow "机器${VIRTNAME}已删除" || red "机器删除失败${VIRTNAME}"
rm -rf ${QEMU_DIR}/${VIRTNAME}.qcow2 > /dev/null 2>&1 && yellow "机器${VIRTNAME}.qcow2已删除" || red "机器${VIRTNAME}.qcow2删除失败"
elif [ "${INPUTID}" == "5" ];then
read -p "增加磁盘大小单位G: " DISKS
case $DISKS in
''|*[!0-9]*)
red "错误:请输入数字!"
exit 1
;;
*)
mkdir -p "${QEMU_DIR}/${VIRTNAME}"
# 获取可用设备名和文件名
TARGET=$(get_next_target)
DISK_PATH=$(get_next_filename)
# 显示信息
yellow "准备添加数据盘:"
echo " 设备名: ${TARGET}"
echo " 磁盘文件: ${DISK_PATH}"
echo " 大小: ${DISKS}G"
# 创建磁盘
if qemu-img create -f qcow2 "${DISK_PATH}" ${DISKS}G > /dev/null 2>&1; then
yellow "磁盘创建成功!"
else
red "磁盘创建失败!"
exit 1
fi
# 挂载磁盘
if virsh attach-disk ${VIRTNAME} "${DISK_PATH}" ${TARGET} --live --persistent --subdriver=qcow2 > /dev/null 2>&1; then
yellow "磁盘挂载成功!"
echo ""
yellow "当前磁盘列表:"
virsh domblklist ${VIRTNAME}
else
red "磁盘挂载失败!"
exit 1
fi
;;
esac
elif [ "${INPUTID}" == "6" ];then
# 获取数据盘列表(排除vda)
DISKS=$(virsh domblklist ${VIRTNAME} 2>/dev/null | awk 'NR>2 && $1 != "vda" && $1 != "" {print $1}')
if [ -z "$DISKS" ]; then
red "没有可卸载的数据盘"
exit 1
fi
yellow "============= 请选择需要卸载的磁盘 ============="
select DISK in $DISKS; do
if [ -n "$DISK" ]; then
yellow "准备卸载: $DISK"
read -p "确认? (y/n): " confirm
if [ "$confirm" = "y" ]; then
# 先获取磁盘文件路径(在卸载前获取)
DISK_PATH=$(virsh domblklist ${VIRTNAME} --details 2>/dev/null | awk -v d="$DISK" '$3==d {print $4}')
# 卸载磁盘
if virsh detach-disk ${VIRTNAME} ${DISK} --live --persistent > /dev/null 2>&1; then
yellow "✓ 磁盘 ${DISK} 卸载成功"
# 删除对应的磁盘文件
if [ -n "$DISK_PATH" ] && [ -f "$DISK_PATH" ]; then
rm -f "$DISK_PATH"
yellow "✓ 磁盘文件已删除: ${DISK_PATH}"
else
yellow "磁盘文件不存在: ${DISK_PATH}"
fi
else
red "✗ 磁盘 ${DISK} 卸载失败"
fi
fi
break
else
red "无效选择"
fi
done
virsh domblklist ${VIRTNAME}
else
red "请输入(1-6)正确的数字"
exit 1
fi
}
# 获取已使用的设备名
get_next_target() {
local used_targets=$(virsh domblklist ${VIRTNAME} --details 2>/dev/null | awk 'NR>2 {print $3}' | grep -E '^vd[a-z]+$')
local letters=("b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z")
for letter in "${letters[@]}"; do
if ! echo "$used_targets" | grep -q "vd${letter}"; then
echo "vd${letter}"
return 0
fi
done
echo "vdb"
}
# 获取不重复的文件名
get_next_filename() {
local base="${QEMU_DIR}/${VIRTNAME}/${VIRTNAME}_data.qcow2"
local counter=1
local filename="$base"
# 如果基础文件不存在,直接返回
if [ ! -f "$filename" ]; then
echo "$filename"
return 0
fi
# 基础文件存在,尝试添加后缀
while [ -f "${QEMU_DIR}/${VIRTNAME}/${VIRTNAME}_data_${counter}.qcow2" ]; do
((counter++))
done
echo "${QEMU_DIR}/${VIRTNAME}/${VIRTNAME}_data_${counter}.qcow2"
}
# 检查端口是否为数字且在范围内,且不冲突
validate_vnc_port() {
local port="$1"
# 1. 判断输入的必须是数字
if [[ ! "$port" =~ ^[0-9]+$ ]]; then
echo "错误: 端口必须是数字!"
return 1
fi
# 2. 数字必须是5900-5999之间
if [[ "$port" -lt 5900 ]] || [[ "$port" -gt 5999 ]]; then
echo "错误: 端口范围必须是 5900-5999 之间!"
return 1
fi
# 3. 选择的端口不能与本地已存在的端口冲突
if ss -tlnp 2>/dev/null | grep -q ":$port " || \
netstat -tlnp 2>/dev/null | grep -q ":$port " || \
lsof -i ":$port" 2>/dev/null | grep -q LISTEN; then
echo "错误: 端口 $port 已被占用!"
return 1
fi
return 0
}
# 创建iso,通过vnc访问安装机器
CREATE_ISO(){
read -r -p "请命名虚拟机名称: " VMNAMES
if [ -z "${VMNAMES}" ];then
red "虚拟机名称不能为空!"
exit 1
else
ISO_NAME=$(ls /data/iso/ | cat)
if [ -z "$ISO_NAME" ]; then
red "没有可用的镜像,请确保iso存放到了 /data/iso/ 目录下!!!"
exit 1
fi
echo ""
yellow "请选择镜像"
select ISO in $ISO_NAME; do
if [ -n "$ISO" ]; then
echo "选择的镜像是: $ISO"
# 循环输入直到正确
while true; do
read -r -p "请选择VNC端口 5900-5999: " VNCPORT
if validate_vnc_port "$VNCPORT"; then
yellow "✓ 端口 $VNCPORT 验证通过"
break
else
red "端口已存在或不在5900-5999之间,请重新输入!"
echo ""
fi
done
read -p "确认? (y/n): " confirm
if [ "$confirm" = "y" ]; then
yellow "虚拟机 ${VMNAMES} 正在创建中..."
virt-install \
--name ${VMNAMES} \
--memory 2048 \
--vcpus 1 \
--cdrom /data/iso/${ISO} \
--disk path=/data/virt-data/${VMNAMES}.qcow2,size=10 \
--network network=default \
--graphics vnc,listen=0.0.0.0,port=${VNCPORT}
# 显示状态
yellow "========================================="
virsh list
yellow "========================================="
yellow "查看日志: tail -f /var/log/libvirt/qemu/${VMNAMES}.log"
yellow "进入控制台: virsh console ${VMNAMES}"
yellow "nvc连接地址: 192.168.4.20:5999"
else
yellow "退出安装iso"
fi
break
else
red "无效选择"
fi
done
fi
}
# 创建虚拟机
CREATE_VM(){
QEMU_FILE="/data/virt-data/kzprd.qcow2"
QEMU_DIR="/data/virt-data"
read -r -p "请输入您要创建的机器名称: " VMNAME
if [ "${VMNAME}" == "" ];then
red "机器名称不能为空!"
exit 1
else
yellow "正在创建机器 ${VMNAME}..."
qemu-img create -f qcow2 -b ${QEMU_FILE} ${QEMU_DIR}/${VMNAME}.qcow2
qemu-img info ${QEMU_DIR}/${VMNAME}.qcow2
fi
read -r -p "确定创建虚拟机?y/n: " YES
if [ "${YES}" == "y" ];then
virt-install \
--name ${VMNAME} \
--memory 2048 \
--vcpus 1 \
--disk path=${QEMU_DIR}/${VMNAME}.qcow2,format=qcow2 \
--import \
--network network=default \
--graphics vnc,listen=0.0.0.0,port=5902 \
--os-variant centos7.0 \
--noautoconsole
else
yellow "即将退出!"
exit 0
fi
}
main(){
yellow "================================"
yellow " 虚拟机管理器 v1.0.0"
yellow "================================"
echo ""
echo " 1. ISO创建初始化机器"
echo " 2. 创建虚拟机"
echo " 3. 管理虚拟机"
echo ""
yellow "================================"
read -r -p "请选择对应的数字(1-3): " STATVM
if [ "${STATVM}" == "1" ];then
CREATE_ISO
elif [ "${STATVM}" == "2" ];then
CREATE_VM
elif [ "${STATVM}" == "3" ];then
DELTE_VM
else
red "无效选择即将退出!"
exit 1
fi
}
main4、执行mvstat脚本

● 先选择1,安装iso,通过vnc连接安装iso镜像
● 安装完成重启virsh虚拟机后,使用vnc连接开启虚拟机的ttyS0
# virsh console使用,进入虚拟机执行命令
systemctl enable serial-getty@ttyS0.service
systemctl start serial-getty@ttyS0.service# 让IP自动获取开启如下
vi /etc/sysconfig/network-scripts/ifcfg-eth0
ONBOOT=yes
systemctl restart network
评论 (0)