feat: 添加 OpenStack 3-5 计算节点自动化评分与自测脚本
This commit is contained in:
1 parent
dcbfac4bed
commit
fc063cd56c
1 file changed
+306
Executable
+306
@@ -0,0 +1,306 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 3-5 计算节点评分:CentOS 7.9 / OpenStack Rocky,Bash 4+,满分 100。
|
||||||
|
# 用法:在 compute 上执行 bash 3-5-compute.sh
|
||||||
|
# 可选:CHECK_CONTROLLER_IP=控制节点管理IP(默认从 hosts 读取,否则 192.168.30.129)
|
||||||
|
# CHECK_COMPUTE_IP=计算节点管理IP(默认从 hosts 读取)
|
||||||
|
# CHECK_REPO_BASE_URL=软件源根URL(默认 http://192.168.192.205:3080)
|
||||||
|
# 只查询当前状态;不安装软件、改配置、重启服务或重建缓存。
|
||||||
|
# 无法从节点内证明 VMware 操作、克隆或命令历史,不为这些过程评分。
|
||||||
|
# 退出码:0=100分,1=有扣分,2=权限/基础工具/参数错误,未完成评分。
|
||||||
|
# 分值:安全8、主机与网络12、软件源15、NTP15、Nova软件包5、
|
||||||
|
# Nova配置30、虚拟化5、服务10。每个配置小组内全部通过才获得该小组分值。
|
||||||
|
export LANG=en_US.UTF-8
|
||||||
|
export LC_ALL=en_US.UTF-8
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
SCORE=0
|
||||||
|
TOTAL=0
|
||||||
|
PASS_CNT=0
|
||||||
|
FAIL_CNT=0
|
||||||
|
TASK_CNT=0
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
separator() {
|
||||||
|
printf '%b====================================================%b\n' "$YELLOW" "$NC"
|
||||||
|
}
|
||||||
|
banner() {
|
||||||
|
separator
|
||||||
|
printf '%b %s%b\n' "$YELLOW" "$1" "$NC"
|
||||||
|
separator
|
||||||
|
}
|
||||||
|
section() {
|
||||||
|
TASK_CNT=$((TASK_CNT+1))
|
||||||
|
printf '%b[➜] %d. 检查任务:%s%b\n' "$BLUE" "$TASK_CNT" "$1" "$NC"
|
||||||
|
}
|
||||||
|
error() {
|
||||||
|
printf '%b[✘ 失败] %s%b\n' "$RED" "$1" "$NC"
|
||||||
|
}
|
||||||
|
# 每个评分项分值固定;查询失败/依赖缺失也计入满分,不跳项缩小分母。
|
||||||
|
# 通过和失败各自使用明确的提示,逐项实时显示。
|
||||||
|
check() {
|
||||||
|
local points=$1 success_message=$2 failure_message=$3
|
||||||
|
shift 3
|
||||||
|
TOTAL=$((TOTAL+points))
|
||||||
|
if "$@" >/dev/null 2>&1; then
|
||||||
|
SCORE=$((SCORE+points)); PASS_CNT=$((PASS_CNT+1))
|
||||||
|
printf '%b[✔ 通过] %s(得 %d 分)%b\n' "$GREEN" "$success_message" "$points" "$NC"
|
||||||
|
else
|
||||||
|
FAIL_CNT=$((FAIL_CNT+1))
|
||||||
|
printf '%b[✘ 失败] %s(扣 %d 分)%b\n' "$RED" "$failure_message" "$points" "$NC"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
finish() {
|
||||||
|
printf '\n'
|
||||||
|
separator
|
||||||
|
printf '实验自测检查完毕!通过项: %b%d%b,失败项: %b%d%b\n' "$GREEN" "$PASS_CNT" "$NC" "$RED" "$FAIL_CNT" "$NC"
|
||||||
|
printf '%b总成绩:%d/100 分%b\n' "$YELLOW" "$SCORE" "$NC"
|
||||||
|
if [ "$TOTAL" -ne 100 ]; then
|
||||||
|
error "评分项总分异常:$TOTAL,请联系教师检查脚本。"
|
||||||
|
separator
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [ "$FAIL_CNT" -eq 0 ]; then
|
||||||
|
printf '%b所有检查项均已通过,实验自测完成!%b\n' "$GREEN" "$NC"
|
||||||
|
separator
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
printf '%b你有 %d 处检查未通过,请根据上方红字提示核对实验步骤并修正!%b\n' "$RED" "$FAIL_CNT" "$NC"
|
||||||
|
separator
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
preflight() {
|
||||||
|
if [ "$EUID" -ne 0 ]; then error '请以 root 身份在对应节点执行。未评分。'; exit 2; fi
|
||||||
|
local cmd
|
||||||
|
for cmd in awk grep sort tr timeout; do
|
||||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then error "缺少基础工具 $cmd,无法评分。"; exit 2; fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
valid_ipv4() {
|
||||||
|
printf '%s\n' "$1" | awk -F. '
|
||||||
|
NF!=4 { bad=1 }
|
||||||
|
{ for(i=1;i<=NF;i++) if($i !~ /^[0-9]+$/ || length($i)>3 || $i+0>255) bad=1 }
|
||||||
|
END { exit (NR!=1 || bad) }'
|
||||||
|
}
|
||||||
|
hosts_ip() {
|
||||||
|
awk -v name="$1" '{sub(/#.*/, ""); for(i=2;i<=NF;i++) if($i==name) print $1}' /etc/hosts 2>/dev/null | sort -u
|
||||||
|
}
|
||||||
|
resolved_as() {
|
||||||
|
local addresses
|
||||||
|
valid_ipv4 "$2" || return 1
|
||||||
|
addresses=$(timeout 5 getent ahostsv4 "$1" 2>/dev/null | awk '{print $1}' | sort -u) || return 1
|
||||||
|
[ "$addresses" = "$2" ]
|
||||||
|
}
|
||||||
|
# 按节读取最后一个有效赋值;忽略整行注释,不执行配置文件。
|
||||||
|
ini_value() {
|
||||||
|
awk -v section="$2" -v key="$3" '
|
||||||
|
/^[[:space:]]*[#;]/ { next }
|
||||||
|
{ sub(/\r$/, "") }
|
||||||
|
/^[[:space:]]*\[/ {
|
||||||
|
s=$0; sub(/^[[:space:]]*\[/,"",s); sub(/\].*$/,"",s)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/,"",s); next
|
||||||
|
}
|
||||||
|
s==section && index($0,"=") {
|
||||||
|
k=substr($0,1,index($0,"=")-1); gsub(/^[[:space:]]+|[[:space:]]+$/,"",k)
|
||||||
|
if(k==key) {
|
||||||
|
v=substr($0,index($0,"=")+1)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/,"",v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {print v}' "$1" 2>/dev/null
|
||||||
|
}
|
||||||
|
enabled_service() { [ "$(timeout 10 systemctl is-enabled "$1" 2>/dev/null)" = enabled ]; }
|
||||||
|
active_service() { timeout 10 systemctl is-active --quiet "$1"; }
|
||||||
|
|
||||||
|
banner 'OpenStack 计算节点实验自测与自动化评分脚本'
|
||||||
|
preflight
|
||||||
|
CONF=/etc/nova/nova.conf
|
||||||
|
# 自动检测失败(例如 hosts 冲突)仍继续评分,让解析项正常扣分。
|
||||||
|
CONTROLLER_IP=${CHECK_CONTROLLER_IP:-$(hosts_ip controller)}
|
||||||
|
if [ -z "${CHECK_CONTROLLER_IP:-}" ] && ! valid_ipv4 "$CONTROLLER_IP"; then
|
||||||
|
CONTROLLER_IP=192.168.30.129
|
||||||
|
fi
|
||||||
|
COMPUTE_IP=${CHECK_COMPUTE_IP:-$(hosts_ip compute)}
|
||||||
|
REPO_BASE=${CHECK_REPO_BASE_URL:-http://192.168.192.205:3080}
|
||||||
|
REPO_BASE=${REPO_BASE%/}
|
||||||
|
if ! valid_ipv4 "$CONTROLLER_IP"; then error '控制节点 IP 参数或 hosts 映射无效。未评分。'; exit 2; fi
|
||||||
|
if [ -n "${CHECK_COMPUTE_IP:-}" ] && ! valid_ipv4 "$CHECK_COMPUTE_IP"; then error 'CHECK_COMPUTE_IP 无效。未评分。'; exit 2; fi
|
||||||
|
case "$REPO_BASE" in http://*|https://*) ;; *) error '软件源根 URL 必须使用 http(s)。未评分。'; exit 2;; esac
|
||||||
|
|
||||||
|
section '基础安全设置(8分)'
|
||||||
|
firewall_stopped() { [ "$(timeout 10 systemctl is-active firewalld 2>/dev/null)" = inactive ]; }
|
||||||
|
firewall_disabled() {
|
||||||
|
local state
|
||||||
|
state=$(timeout 10 systemctl is-enabled firewalld 2>/dev/null)
|
||||||
|
case "$state" in disabled|masked) return 0;; *) return 1;; esac
|
||||||
|
}
|
||||||
|
selinux_runtime() { case "$(getenforce 2>/dev/null)" in Disabled|Permissive) return 0;; *) return 1;; esac; }
|
||||||
|
selinux_persistent() {
|
||||||
|
awk '/^[[:space:]]*#/ {next}
|
||||||
|
/^[[:space:]]*SELINUX[[:space:]]*=/ {
|
||||||
|
v=$0; sub(/^[^=]*=/,"",v); sub(/[[:space:]]*#.*/,"",v); gsub(/[[:space:]"\047]/,"",v)
|
||||||
|
} END {exit (v!="disabled")}' /etc/selinux/config
|
||||||
|
}
|
||||||
|
check 2 'firewalld 防火墙已停止' 'firewalld 应停止,查询失败也不得分' firewall_stopped
|
||||||
|
check 2 'firewalld 防火墙已禁止开机启动' 'firewalld 应禁止开机启动' firewall_disabled
|
||||||
|
check 2 'SELinux 当前为 Disabled 或 Permissive' 'SELinux 当前应为 Disabled 或 Permissive' selinux_runtime
|
||||||
|
check 2 'SELinux 持久配置为 disabled' '/etc/selinux/config 应设置 SELINUX=disabled' selinux_persistent
|
||||||
|
|
||||||
|
section '主机名、解析与网络(12分)'
|
||||||
|
hostname_ok() { [ "$(hostname)" = compute ] && [ "$(tr -d '[:space:]' < /etc/hostname)" = compute ]; }
|
||||||
|
controller_mapping() { [ "$(hosts_ip controller)" = "$CONTROLLER_IP" ]; }
|
||||||
|
compute_mapping() {
|
||||||
|
valid_ipv4 "$COMPUTE_IP" && [ "$COMPUTE_IP" != "$CONTROLLER_IP" ] &&
|
||||||
|
[ "$(hosts_ip compute)" = "$COMPUTE_IP" ] &&
|
||||||
|
ip -o -4 addr show scope global | awk -v ip="$COMPUTE_IP" '{split($4,a,"/"); if(a[1]==ip)found=1} END{exit !found}'
|
||||||
|
}
|
||||||
|
both_resolve() { resolved_as controller "$CONTROLLER_IP" && resolved_as compute "$COMPUTE_IP"; }
|
||||||
|
localhost_preserved() { awk '{sub(/#.*/,""); if($1=="127.0.0.1")for(i=2;i<=NF;i++)if($i=="localhost")found=1} END{exit !found}' /etc/hosts; }
|
||||||
|
check 2 '运行中及持久主机名均为 compute' '运行中及持久主机名都应为 compute' hostname_ok
|
||||||
|
check 2 'controller 的 hosts 映射唯一,控制节点 IP 配置正确' '/etc/hosts 中 controller 应唯一映射到控制节点 IP' controller_mapping
|
||||||
|
check 2 'compute 的 hosts 映射唯一,且指向本机管理 IPv4' '/etc/hosts 中 compute 应唯一映射到本机管理 IPv4' compute_mapping
|
||||||
|
check 2 'controller 和 compute 的系统解析与 hosts 配置一致' 'controller/compute 的系统解析应与 hosts 一致' both_resolve
|
||||||
|
check 2 'hosts 中保留了 localhost 的 IPv4 映射' '新增 hosts 条目时应保留 localhost 的 IPv4 映射' localhost_preserved
|
||||||
|
check 2 '可通过主机名 ping 通 controller' '应能通过主机名 ping 通 controller' timeout 8 ping -4 -c 2 -W 2 controller
|
||||||
|
|
||||||
|
section '软件源配置、连通性与缓存(15分)'
|
||||||
|
# 每个仓库分别评价配置、repomd 索引、已有缓存,各1分。
|
||||||
|
shopt -s nullglob
|
||||||
|
REPOS=(/etc/yum.repos.d/*.repo)
|
||||||
|
repo_layout_ok() {
|
||||||
|
[ "${#REPOS[@]}" -gt 0 ] || return 1
|
||||||
|
awk '
|
||||||
|
function flush() {
|
||||||
|
if(s!="" && enabled!="0" && s!="local-base" && s!="local-updates" &&
|
||||||
|
s!="local-extras" && s!="local-qemu-ev" && s!="local-openstack-rocky") bad=1
|
||||||
|
}
|
||||||
|
FNR==1 {flush(); s=""; enabled="1"}
|
||||||
|
/^[[:space:]]*[#;]/ {next}
|
||||||
|
/^[[:space:]]*\[/ {
|
||||||
|
flush(); s=$0; sub(/^[[:space:]]*\[/,"",s); sub(/\].*$/,"",s)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/,"",s); enabled="1"; next
|
||||||
|
}
|
||||||
|
/^[[:space:]]*enabled[[:space:]]*=/ {
|
||||||
|
enabled=$0; sub(/^[^=]*=/,"",enabled); sub(/[[:space:]]+[#;].*$/,"",enabled)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/,"",enabled)
|
||||||
|
}
|
||||||
|
END{flush(); exit bad}' "${REPOS[@]}"
|
||||||
|
}
|
||||||
|
repo_ok() {
|
||||||
|
local file=$1 id=$2 url=$3 key value count excludes
|
||||||
|
[ -r "$file" ] && repo_layout_ok || return 1
|
||||||
|
count=$(awk -v id="$id" '/^[[:space:]]*\[/ {s=$0; sub(/^[[:space:]]*\[/,"",s); sub(/\].*$/,"",s); gsub(/^[[:space:]]+|[[:space:]]+$/,"",s); if(s==id)n++} END{print n+0}' "${REPOS[@]}") || return 1
|
||||||
|
[ "$count" = 1 ] || return 1
|
||||||
|
value=$(ini_value "$file" "$id" baseurl)
|
||||||
|
[ "${value%/}" = "${url%/}" ] && [ "$(ini_value "$file" "$id" enabled)" = 1 ] &&
|
||||||
|
[ "$(ini_value "$file" "$id" gpgcheck)" = 0 ] || return 1
|
||||||
|
for key in mirrorlist metalink; do [ -z "$(ini_value "$file" "$id" "$key")" ] || return 1; done
|
||||||
|
if [ "$id" = local-openstack-rocky ]; then
|
||||||
|
excludes=$(ini_value "$file" "$id" exclude | tr ',' ' ')
|
||||||
|
printf '%s\n' "$excludes" | awk '{for(i=1;i<=NF;i++){if($i=="sip")s=1;if($i=="PyQt4")p=1}} END{exit !(s&&p)}' || return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
repo_online() {
|
||||||
|
local data
|
||||||
|
data=$(curl --noproxy '*' -fsSL --connect-timeout 3 --max-time 10 --max-filesize 1048576 "$1/repodata/repomd.xml") || return 1
|
||||||
|
printf '%s\n' "$data" | grep -Eq '<repomd([[:space:]>])' && printf '%s\n' "$data" | grep -Fq '</repomd>'
|
||||||
|
}
|
||||||
|
repo_cache() {
|
||||||
|
timeout 20 yum -C --noplugins --disablerepo='*' --enablerepo="$1" --setopt="$1.skip_if_unavailable=0" list available
|
||||||
|
}
|
||||||
|
while IFS='|' read -r file id path; do
|
||||||
|
url="$REPO_BASE/$path"
|
||||||
|
check 1 "[$id] 软件源配置正确,定义唯一,且未遗留其他启用源" "[$id] 配置缺失、重复或遗留其他启用源(Rocky 还需 exclude=sip,PyQt4)" repo_ok "/etc/yum.repos.d/$file" "$id" "$url"
|
||||||
|
check 1 "[$id] 仓库索引可正常访问:$url" "[$id] 仓库索引不可访问:$url" repo_online "$url"
|
||||||
|
check 1 "[$id] 已有 YUM 缓存可正常读取" "[$id] 已有 YUM 缓存不可读取,请检查 yum makecache 结果" repo_cache "$id"
|
||||||
|
done <<'REPOLIST'
|
||||||
|
CentOS-Base.repo|local-base|vault-base
|
||||||
|
CentOS-Base.repo|local-updates|vault-updates
|
||||||
|
CentOS-Base.repo|local-extras|vault-extras
|
||||||
|
CentOS-QEMU-EV.repo|local-qemu-ev|vault-centos-qemu-ev
|
||||||
|
CentOS-OpenStack-rocky.repo|local-openstack-rocky|vault-centos-openstack-rocky
|
||||||
|
REPOLIST
|
||||||
|
|
||||||
|
section 'NTP 时间同步(15分)'
|
||||||
|
chrony_config() {
|
||||||
|
awk '{sub(/[#!;].*/,"")}
|
||||||
|
$1=="server" && $2=="controller" {n++; for(i=3;i<=NF;i++)if($i=="iburst")good++}
|
||||||
|
($1=="server" || $1=="pool" || $1=="peer") && !($1=="server" && $2=="controller") {bad=1}
|
||||||
|
END{exit !(n==1 && good==1 && !bad)}' /etc/chrony.conf
|
||||||
|
}
|
||||||
|
chrony_synced() {
|
||||||
|
local sources
|
||||||
|
sources=$(timeout 10 chronyc -n sources) || return 1
|
||||||
|
printf '%s\n' "$sources" | awk -v ip="$CONTROLLER_IP" '
|
||||||
|
$1=="^*" && $2==ip {found=1}
|
||||||
|
$1 ~ /^[\^=]/ && $2!=ip {bad=1}
|
||||||
|
END{exit !(found && !bad)}'
|
||||||
|
}
|
||||||
|
check 2 'chrony 时间同步软件包已安装' 'chrony 软件包未安装' rpm -q chrony
|
||||||
|
check 4 'chrony 仅启用了 server controller iburst,默认外网源已注释' 'chrony 应仅启用 server controller iburst,并注释默认外网源' chrony_config
|
||||||
|
check 2 'chronyd 时间同步服务正在运行' 'chronyd 未运行' active_service chronyd
|
||||||
|
check 2 'chronyd 时间同步服务已设置持久开机自启' 'chronyd 未持久设置开机自启' enabled_service chronyd
|
||||||
|
check 5 'chronyd 已选中控制节点为同步源(^*),且未加载其他服务器源' 'chronyd 当前应选中控制节点为同步源(^*),且没有其他服务器源' chrony_synced
|
||||||
|
|
||||||
|
section 'Nova 软件包(5分)'
|
||||||
|
check 5 'openstack-nova-compute 软件包已安装' 'openstack-nova-compute 未安装' rpm -q openstack-nova-compute
|
||||||
|
|
||||||
|
section 'Nova 配置(30分)'
|
||||||
|
# 配置按小组评分;组内错误键附在失败提示中,不输出实际值或口令。
|
||||||
|
config_group() {
|
||||||
|
local points=$1 group=$2 key expected actual bad=''
|
||||||
|
shift 2
|
||||||
|
while [ "$#" -ge 2 ]; do
|
||||||
|
key=$1 expected=$2; shift 2
|
||||||
|
actual=$(ini_value "$CONF" "$group" "$key")
|
||||||
|
case "$key" in
|
||||||
|
enabled|use_neutron)
|
||||||
|
actual=$(printf '%s' "$actual" | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$actual" in 1|yes|on) actual=true;; esac;;
|
||||||
|
project_domain_name|user_domain_name)
|
||||||
|
actual=$(printf '%s' "$actual" | tr '[:upper:]' '[:lower:]')
|
||||||
|
expected=$(printf '%s' "$expected" | tr '[:upper:]' '[:lower:]');;
|
||||||
|
enabled_apis)
|
||||||
|
# APIs 的顺序无关;仅接受手册要求的这两个 API。
|
||||||
|
actual=$(ini_value "$CONF" "$group" "$key" | tr ',' '\n' | tr -d ' \t\r' | sort | tr '\n' ',')
|
||||||
|
expected='metadata,osapi_compute,';;
|
||||||
|
server_proxyclient_address)
|
||||||
|
[ "$actual" != '$my_ip' ] || actual=$(ini_value "$CONF" DEFAULT my_ip);;
|
||||||
|
auth_url|api_servers) actual=${actual%/};;
|
||||||
|
esac
|
||||||
|
if [ -z "$actual" ] || [ "$actual" != "$expected" ]; then bad="$bad $key"; fi
|
||||||
|
done
|
||||||
|
# my_ip 必须同时属于本机;即便 hosts 未完成,也不接受示例中的远端 IP。
|
||||||
|
if [ "$group" = DEFAULT ] && ! compute_mapping >/dev/null 2>&1; then bad="$bad my_ip/hosts/本机地址"; fi
|
||||||
|
check "$points" "[$group] Nova 配置符合实验要求" "[$group] 错误或缺失的配置键:${bad:-文件不可读};请检查 $CONF" test -z "$bad"
|
||||||
|
}
|
||||||
|
config_group 6 DEFAULT enabled_apis 'osapi_compute,metadata' transport_url 'rabbit://openstack:openstack@controller' my_ip "$COMPUTE_IP" use_neutron true firewall_driver nova.virt.firewall.NoopFirewallDriver
|
||||||
|
config_group 2 api auth_strategy keystone
|
||||||
|
config_group 6 keystone_authtoken auth_url http://controller:5000/v3 memcached_servers controller:11211 auth_type password project_domain_name Default user_domain_name Default project_name service username nova password nova
|
||||||
|
config_group 6 vnc enabled true server_listen 0.0.0.0 server_proxyclient_address "$COMPUTE_IP" novncproxy_base_url "http://$CONTROLLER_IP:6080/vnc_auto.html"
|
||||||
|
config_group 3 glance api_servers http://controller:9292
|
||||||
|
config_group 2 oslo_concurrency lock_path /var/lib/nova/tmp
|
||||||
|
config_group 5 placement region_name RegionOne project_domain_name Default project_name service auth_type password user_domain_name Default auth_url http://controller:5000/v3 username placement password placement
|
||||||
|
|
||||||
|
section '虚拟化类型(5分)'
|
||||||
|
virtualization_ok() {
|
||||||
|
local type
|
||||||
|
[ -r /proc/cpuinfo ] && [ -r "$CONF" ] || return 1
|
||||||
|
type=$(ini_value "$CONF" libvirt virt_type)
|
||||||
|
if grep -Eq '(^|[[:space:]])(vmx|svm)([[:space:]]|$)' /proc/cpuinfo; then
|
||||||
|
# Rocky 默认为 kvm;显式使用 qemu 也可运行手册的计算服务。
|
||||||
|
case "$type" in ''|kvm|qemu) return 0;; *) return 1;; esac
|
||||||
|
fi
|
||||||
|
[ "$type" = qemu ]
|
||||||
|
}
|
||||||
|
check 5 'Nova 虚拟化类型与 CPU 硬件虚拟化能力匹配' '无 vmx/svm 时必须设置 [libvirt] virt_type=qemu;有扩展时允许默认 kvm 或 qemu' virtualization_ok
|
||||||
|
|
||||||
|
section '计算服务运行与开机自启(10分)'
|
||||||
|
for service in libvirtd openstack-nova-compute; do
|
||||||
|
check 3 "$service 服务正在运行" "$service 未运行" active_service "$service"
|
||||||
|
check 2 "$service 服务已设置持久开机自启" "$service 未持久设置开机自启" enabled_service "$service"
|
||||||
|
done
|
||||||
|
finish
|
||||||
Reference in new issue
Block a user