e20c/e20c/kvm.sh

452 lines
15 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 定义颜色输出函数
red() { echo -e "\033[31m\033[01m[WARNING] $1\033[0m"; }
green() { echo -e "\033[32m\033[01m[INFO] $1\033[0m"; }
greenline() { echo -e "\033[32m\033[01m $1\033[0m"; }
yellow() { echo -e "\033[33m\033[01m[NOTICE] $1\033[0m"; }
blue() { echo -e "\033[34m\033[01m[MESSAGE] $1\033[0m"; }
light_magenta() { echo -e "\033[95m\033[01m[NOTICE] $1\033[0m"; }
magenta() { echo -e "\033[95m\033[01m$1\033[0m"; }
highlight() { echo -e "\033[32m\033[01m$1\033[0m"; }
cyan() { echo -e "\033[38;2;0;255;255m$1\033[0m"; }
# 检查是否以 root 用户身份运行
if [ "$(id -u)" -ne 0 ]; then
green "注意!输入密码过程不显示*号属于正常现象"
echo "此脚本需要以 root 用户权限运行,请输入当前用户的密码:"
# 使用 'sudo' 重新以 root 权限运行此脚本
sudo -E "$0" "$@"
exit $?
fi
declare -a menu_options
declare -A commands
menu_options=(
"更新Linux系统软件包"
"安装文件管理器FileBrowser"
"安装QEMU/KVM虚拟机管理器"
"安装Cockpit虚拟机Web管理工具"
"允许虚拟机通过指定的桥接网卡收发数据"
"安装docker"
"安装1panel面板管理工具"
"卸载1panel面板管理工具"
"卸载QEMU/KVM虚拟机管理器"
"卸载Cockpit虚拟机Web管理工具"
"***升级e20c内核到6.1***"
"更新脚本"
)
commands=(
["更新Linux系统软件包"]="update_system_packages"
["安装文件管理器FileBrowser"]="install_filemanager"
["安装QEMU/KVM虚拟机管理器"]="install_virt_manager"
["安装Cockpit虚拟机Web管理工具"]="install_cockpit"
["允许虚拟机通过指定的桥接网卡收发数据"]="add_nft_rules_for_bridge"
["安装docker"]="install_docker"
["安装1panel面板管理工具"]="install_1panel_on_linux"
["卸载QEMU/KVM虚拟机管理器"]="uninstall_virt_manager"
["卸载Cockpit虚拟机Web管理工具"]="uninstall_cockpit"
["卸载1panel面板管理工具"]="uninstall_1panel"
["***升级e20c内核到6.1***"]="update_e20c_kernel"
["更新脚本"]="update_scripts"
)
update_e20c_kernel() {
sudo armbian-update -k 6.1
}
# 更新系统软件包
update_system_packages() {
green "Setting timezone Asia/Shanghai..."
sudo timedatectl set-timezone Asia/Shanghai
# 更新系统软件包
green "Updating system packages..."
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
if ! command -v curl &>/dev/null; then
red "curl is not installed. Installing now..."
sudo apt install -y curl
if command -v curl &>/dev/null; then
green "curl has been installed successfully."
else
echo "Failed to install curl. Please check for errors."
fi
else
echo "curl is already installed."
fi
green "软件包更新完成。建议重启后再完成后续的操作"
}
# 安装docker
install_docker() {
bash <(curl -sSL https://linuxmirrors.cn/docker.sh)
}
# 安装QEMU/KVM虚拟机管理器
install_virt_manager() {
sudo apt-get install -y gconf2 qemu-system-arm qemu-utils qemu-efi ipxe-qemu libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager seabios vgabios gir1.2-spiceclientgtk-3.0 xauth fonts-wqy-microhei
sudo apt install qemu-kvm -y
check_kvm
}
check_kvm() {
if [ -c /dev/kvm ]; then
green "恭喜!KVM 安装成功"
else
red "KVM未能安装成功 可能是您的系统内核不支持KVM 或者是 该设备的CPU不支持KVM"
fi
}
# 卸载QEMU/KVM虚拟机管理器
uninstall_virt_manager() {
sudo apt-get purge -y gconf2 qemu-system-arm qemu-utils qemu-efi ipxe-qemu libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager seabios vgabios gir1.2-spiceclientgtk-3.0 xauth fonts-wqy-microhei
sudo apt-get autoremove -y
sudo apt-get clean
}
# 安装Cockpit虚拟机Web管理工具
install_cockpit() {
sudo apt install cockpit cockpit-machines cockpit-networkmanager -y
sudo systemctl start cockpit
sudo apt install nftables -y
nft add table ip filter
nft add chain ip filter FORWARD { type filter hook forward priority 0 \; }
sudo apt install dnsmasq -y
sudo apt install dmidecode -y
green "http://<您的服务器IP>:9090 或 https://$(hostname):9090"
}
# 卸载Cockpit虚拟机Web管理工具
uninstall_cockpit() {
sudo systemctl stop cockpit
sudo apt-get purge -y cockpit cockpit-machines cockpit-networkmanager nftables dnsmasq dmidecode
sudo apt-get autoremove -y
sudo apt-get clean
sudo nft delete table ip filter
}
# 允许虚拟机通过指定的桥接网卡收发数据
add_nft_rules_for_bridge() {
apt install nftables -y
nft add table ip filter
nft add chain ip filter FORWARD { type filter hook forward priority 0 \; }
read -p "请输入桥接网卡名称: " bridge_name
nft add rule ip filter FORWARD iifname "$bridge_name" accept
nft add rule ip filter FORWARD oifname "$bridge_name" accept
sudo nft list ruleset > /etc/nftables.conf
sudo systemctl enable nftables
green "$bridge_name 防火墙规则已设置"
}
# 安装文件管理器
# 源自 https://filebrowser.org/installation
install_filemanager() {
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR
filemanager_os="unsupported"
filemanager_arch="unknown"
install_path="/usr/local/bin"
# Termux on Android has $PREFIX set which already ends with /usr
if [[ -n "$ANDROID_ROOT" && -n "$PREFIX" ]]; then
install_path="$PREFIX/bin"
fi
# Fall back to /usr/bin if necessary
if [[ ! -d $install_path ]]; then
install_path="/usr/bin"
fi
# Not every platform has or needs sudo (https://termux.com/linux.html)
((EUID)) && [[ -z "$ANDROID_ROOT" ]] && sudo_cmd="sudo"
#########################
# Which OS and version? #
#########################
filemanager_bin="filebrowser"
filemanager_dl_ext=".tar.gz"
# NOTE: `uname -m` is more accurate and universal than `arch`
# See https://en.wikipedia.org/wiki/Uname
unamem="$(uname -m)"
case $unamem in
*aarch64*)
filemanager_arch="arm64"
;;
*64*)
filemanager_arch="amd64"
;;
*86*)
filemanager_arch="386"
;;
*armv5*)
filemanager_arch="armv5"
;;
*armv6*)
filemanager_arch="armv6"
;;
*armv7*)
filemanager_arch="armv7"
;;
*)
green "Aborted, unsupported or unknown architecture: $unamem"
return 2
;;
esac
unameu="$(tr '[:lower:]' '[:upper:]' <<<$(uname))"
if [[ $unameu == *DARWIN* ]]; then
filemanager_os="darwin"
elif [[ $unameu == *LINUX* ]]; then
filemanager_os="linux"
elif [[ $unameu == *FREEBSD* ]]; then
filemanager_os="freebsd"
elif [[ $unameu == *NETBSD* ]]; then
filemanager_os="netbsd"
elif [[ $unameu == *OPENBSD* ]]; then
filemanager_os="openbsd"
elif [[ $unameu == *WIN* || $unameu == MSYS* ]]; then
# Should catch cygwin
sudo_cmd=""
filemanager_os="windows"
filemanager_bin="filebrowser.exe"
filemanager_dl_ext=".zip"
else
green "Aborted, unsupported or unknown OS: $uname"
return 6
fi
green "正在下载文件管理器($filemanager_os/$filemanager_arch) 请稍等..."
if type -p curl >/dev/null 2>&1; then
net_getter="curl -fsSL"
elif type -p wget >/dev/null 2>&1; then
net_getter="wget -qO-"
else
green "Aborted, could not find curl or wget"
return 7
fi
filemanager_file="${filemanager_os}-$filemanager_arch-filebrowser$filemanager_dl_ext"
filemanager_url="https://wkrepo.vip.cpolar.cn/res/$filemanager_file"
# Use $PREFIX for compatibility with Termux on Android
rm -rf "$PREFIX/tmp/$filemanager_file"
${net_getter} "$filemanager_url" >"$PREFIX/tmp/$filemanager_file"
green "下载完成 正在解压..."
case "$filemanager_file" in
*.zip) unzip -o "$PREFIX/tmp/$filemanager_file" "$filemanager_bin" -d "$PREFIX/tmp/" ;;
*.tar.gz) tar -xzf "$PREFIX/tmp/$filemanager_file" -C "$PREFIX/tmp/" "$filemanager_bin" ;;
esac
chmod +x "$PREFIX/tmp/$filemanager_bin"
$sudo_cmd mv "$PREFIX/tmp/$filemanager_bin" "$install_path/$filemanager_bin"
if setcap_cmd=$(PATH+=$PATH:/sbin type -p setcap); then
$sudo_cmd $setcap_cmd cap_net_bind_service=+ep "$install_path/$filemanager_bin"
fi
$sudo_cmd rm -- "$PREFIX/tmp/$filemanager_file"
if type -p $filemanager_bin >/dev/null 2>&1; then
light_magenta "不依赖于docker的 文件管理器安装成功"
trap ERR
start_filemanager
return 0
else
red "Something went wrong, File Browser is not in your path"
trap ERR
return 1
fi
}
# 启动文件管理器
start_filemanager() {
# 检查是否已经安装 filebrowser
if ! command -v filebrowser &>/dev/null; then
red "Error: filebrowser 未安装,请先安装 filebrowser"
return 1
fi
# Add configuration file generation and editing
$sudo_cmd mkdir -p /etc/filebrowser
$sudo_cmd touch /etc/filebrowser/.filebrowser.json
$sudo_cmd chown $(id -u):$(id -g) /etc/filebrowser/.filebrowser.json
# Set the desired port
desired_port="38080"
cat >/etc/filebrowser/.filebrowser.json <<EOF
{
"port": "$desired_port",
"root": "/etc/filebrowser",
"database": "/etc/filebrowser/filebrowser.db",
"auth": {
"username": "admin",
"password": "admin"
}
}
EOF
green "设置文件管理器的端口为: $desired_port"
# 启动 filebrowser 文件管理器
green "启动 filebrowser 文件管理器..."
# 使用 nohup 和输出重定向,记录启动日志到 filebrowser.log 文件中
nohup sudo filebrowser -r / --address 0.0.0.0 --port $desired_port >filebrowser.log 2>&1 &
# 检查 filebrowser 是否成功启动
if [ $? -ne 0 ]; then
red "Error: 启动 filebrowser 文件管理器失败"
return 1
fi
local host_ip
host_ip=$(hostname -I | awk '{print $1}')
green "filebrowser 文件管理器已启动,可以通过 http://${host_ip}:${desired_port} 访问"
green "登录用户名admin"
green "默认密码admin请尽快修改密码"
# 创建 Systemd 服务文件
cat >/etc/systemd/system/filebrowser.service <<EOF
[Unit]
Description=File Browser Service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/filebrowser -r / --address 0.0.0.0 --port ${desired_port}
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo chmod +x /etc/systemd/system/filebrowser.service
sudo systemctl daemon-reload # 重新加载systemd配置
sudo systemctl start filebrowser.service # 启动服务
sudo systemctl enable filebrowser.service # 设置开机启动
sudo systemctl restart NetworkManager # 重启网络 保证hostname生效
yellow "已设置文件管理器开机自启动,下次开机可直接访问文件管理器"
SCRIPT_PATH="/usr/trim/bin/show_startup_info.sh"
# 判断脚本是否存在
if [ ! -f "$SCRIPT_PATH" ]; then
return 1
fi
HOST_NAME=$(hostname)
cp "$SCRIPT_PATH" "${SCRIPT_PATH}.bak"
# 在飞牛OS开机命令行界面插入Filebrowser地址和端口信息
INSERT_INFO="Filebrowser Web console: http://$HOST_NAME:$desired_port or http://${host_ip}:${desired_port}\n"
sed -i "/^Filebrowser Web console/d" "$SCRIPT_PATH"
sed -i "/INFO_CONTENT=/a $INSERT_INFO" "$SCRIPT_PATH"
light_magenta "文件管理器的访问地址和端口 已追加到飞牛OS开机命令行界面 预览如下"
bash "$SCRIPT_PATH"
cat /etc/issue
}
# 安装1panel面板
install_1panel_on_linux() {
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
intro="https://1panel.cn/docs/installation/cli/"
if command -v 1pctl &>/dev/null; then
echo '{
"registry-mirrors": [
"https://docker.1panel.live"
]
}' | sudo tee /etc/docker/daemon.json >/dev/null
sudo /etc/init.d/docker restart
green "如何卸载1panel 请参考:$intro"
else
red "未安装1panel"
fi
}
# 卸载1panel
uninstall_1panel() {
sudo 1pctl uninstall
}
# 更新自己
update_scripts() {
wget -O kvm.sh https://cafe.cpolar.cn/wkdaily/e20c/raw/branch/master/e20c/kvm.sh && chmod +x kvm.sh
echo "脚本已更新并保存在当前目录 kvm.sh,现在将执行新脚本。"
./kvm.sh
exit 0
}
check_cpu_architecture() {
ARCH=$(uname -m)
if [[ "$ARCH" == "x86_64" ]]; then
magenta " 当前CPU型号:$(grep -m 1 "model name" /proc/cpuinfo | awk -F ': ' '{print $2}')"
elif [[ "$ARCH" == "aarch64" || "$ARCH" == "armv7l" ]]; then
if [[ -f /proc/device-tree/model ]]; then
magenta " 当前ARM机型:$(cat /proc/device-tree/model | tr -d '\0')"
else
echo -e " Board model information not available."
fi
else
echo -e " Unknown architecture: $ARCH"
fi
}
show_menu() {
clear
greenline "————————————————————————————————————————————————————"
echo '
*********** QEMU/KVM 虚拟机管理助手 **********
环境: (Ubuntu/Debian/Armbian/RaspberryPiOS/fnOS etc)
平台: arm64/x86-64'
check_cpu_architecture
echo -e " https://github.com/wukongdaily/e20c/"
greenline "————————————————————————————————————————————————————"
echo "请选择操作:"
# 特殊处理的项数组
special_items=("安装QEMU/KVM虚拟机管理器" "安装Cockpit虚拟机Web管理工具" "安装文件管理器FileBrowser")
for i in "${!menu_options[@]}"; do
if [[ " ${special_items[*]} " =~ " ${menu_options[i]} " ]]; then
# 如果当前项在特殊处理项数组中,使用特殊颜色
cyan "$((i + 1)). ${menu_options[i]}"
else
# 否则,使用普通格式
echo "$((i + 1)). ${menu_options[i]}"
fi
done
}
handle_choice() {
local choice=$1
# 检查输入是否为空
if [[ -z $choice ]]; then
echo -e "${RED}输入不能为空,请重新选择。${NC}"
return
fi
# 检查输入是否为数字
if ! [[ $choice =~ ^[0-9]+$ ]]; then
echo -e "${RED}请输入有效数字!${NC}"
return
fi
# 检查数字是否在有效范围内
if [[ $choice -lt 1 ]] || [[ $choice -gt ${#menu_options[@]} ]]; then
echo -e "${RED}选项超出范围!${NC}"
echo -e "${YELLOW}请输入 1 到 ${#menu_options[@]} 之间的数字。${NC}"
return
fi
# 执行命令
if [ -z "${commands[${menu_options[$choice - 1]}]}" ]; then
echo -e "${RED}无效选项,请重新选择。${NC}"
return
fi
"${commands[${menu_options[$choice - 1]}]}"
}
while true; do
show_menu
read -p "请输入选项的序号(输入q退出): " choice
if [[ $choice == 'q' ]]; then
break
fi
handle_choice $choice
echo "按任意键继续..."
read -n 1 # 等待用户按键
done