Megacli 批量磁盘巡检

情况基本介绍

原理:根据megacli -ldpdinfo -aALL -Nolog命令输出的Count错误计数来判断磁盘预错误信息,以实现故障提前处置,避免磁盘连续非预期离线,导致存储集群服务故障。

背景:

现网生产环境,不允许安装额外的软件。
smartctl版本较老且不允许升级,部分smart参数不能识别。
python版本2.7和熟悉的python3差别也挺大,所以选择用shell编写脚本。

步骤:

定义输出目标文件,和结果计数对象。
从host文件里读取server列表,并遍历执行megacli命令。
对megacli的输出进行格式化和计数,并在屏幕中反馈故障信息。

环境要求:

脚本执行主机需要有免密登录目标主机的权限。
目标主机需要有megacli工具,如果有storcli,可以对脚本的计数、匹配语句稍作修改,且storcli功能更强。

目前脚本的不足,和下一步的改进方向:

目前未统计故障硬盘数量,后续可根据磁盘ID等信息统计故障数量并输出。
未对故障信息进行转存记录。
未联动告警功能。

megacli命令过滤后的原始输出:

Virtual Drive: 0 (Target Id: 0)
RAID Level          : Primary-1, Secondary-0, RAID Level Qualifier-0
Size                : 446.625 GB
State               : Optimal
Enclosure Device ID: 8
Slot Number: 38
Device Id: 24
Media Error Count: 0
Other Error Count: 0
Predictive Failure Count: 0
Firmware state: Online, Spun Up
Drive Temperature :31C (87.80 F)
Enclosure Device ID: 8
Slot Number: 39
Device Id: 34
Media Error Count: 0
Other Error Count: 0
Predictive Failure Count: 0
Firmware state: Online, Spun Up
Drive Temperature :33C (91.40 F)

shell脚本内容:

#!/bin/bash
# 定义一个空数组failHost,用于存储执行失败的主机
failHost=()

# 判断当前目录下是否有名为“年月日-时分秒-OsdDiskCheck.csv”的文件,如果没有,就新建一个:
ResultFile="$(date +%Y%m%d-%H%M%S)-OsdDiskCheck.csv"
if [ ! -f "$ResultFile" ]; then
    touch "$ResultFile"
else
    echo "$ResultFile 巡检结果文件已存在,请检查是否需要删除!"
fi

# 读取主机列表文件内容到数组
mapfile -t hosts < "osdServerList.txt"
hostsLen="${#hosts[@]}"
hostsSuc=0

# 遍历数组中的每一行主机名
for line in "${hosts[@]}"; do
    echo "----------ssh $line----------"
    # 尝试在远端主机$line 上执行命令:
    if ! diskOutPut=`ssh $line 'megacli -ldpdinfo -aALL -Nolog | egrep -i "Virtual Drive|RAID Level|^Size|^State|Enclosure Device|Slot Number|Device Id|media error|other error|^Predictive|Firmware stat|Temperature"' && sleep 5`
    then
        echo "$line 巡检失败,请检查"
        failHost+=(" $line")
    else
    hostsSuc=$((hostsSuc+1))
    # 1. 分割outPut对象,:做为分隔符,之前的字符定义为key,之后的字符为value
    while IFS=: read -r key value; do
        key=`echo $key | awk -F'  ' '{print $1}'`
        value="${value#"${value%%[![:space:]]*}"}"
        # 对命令输出里的Count(错误计数)进行信息组合,加上raid ID和eid、sid
        if [[ $key =~ "Virtual Drive" ]]; then
            keyVirt="RaidID_`echo $value|awk '{print $1}'`"
        elif [[ $key =~ "Enclosure Device ID" ]]; then
            keyEID="E_$value"
        elif [[ $key =~ "Slot Number" ]]; then
            keySID="S_$value"
        elif [[ $key =~ "Count" ]]; then
            key="Host: $line, $keyVirt, $keyEID~$keySID, $key"
            # 如果value不为0,就输出:“发现错误:$key---$value”
            if [[ $value != 0 ]]; then
                echo "发现磁盘错误:$key---$value"
                if [[ $key =~ "Firmware state" ]] && [[ $value =~ "Online" ]]; then
                    echo "该告警磁盘仍然在线,请密切关注,确认是否手动踢盘!"
                else
                    echo "该告警磁盘已处于故障状态!"
                fi
            fi
        else
            key="$key"
        fi
        echo "$key---$value" >> "$ResultFile"
    done <<<"$diskOutPut"
    fi
done

# 判断failHost数组是否为空,如果为空,就打印“所有主机巡检执行成功”,否则打印出“执行失败的主机:${failHost[@]}”:
echo "主机列表共 $hostsLen 台主机,巡检执行成功 $hostsSuc 台!"
if [ ${#failHost[@]} -ne 0 ]; then
    echo "执行失败的主机有:${failHost[@]}。"
fi

执行输出:

……
----------ssh z***3----------
Warning: Permanently added 'z***3' (ECDSA) to the list of known hosts.
发现磁盘错误:Host: z***3, RaidID_1, E_8~S_0, Other Error Count---6
该告警磁盘已处于故障状态!
发现磁盘错误:Host: z***3, RaidID_6, E_8~S_5, Other Error Count---3
该告警磁盘已处于故障状态!
发现磁盘错误:Host: z***3, RaidID_9, E_8~S_8, Other Error Count---7
该告警磁盘已处于故障状态!
----------ssh z***4----------
Warning: Permanently added 'z***4' (ECDSA) to the list of known hosts.
----------ssh z***7----------
Warning: Permanently added 'z***7' (ECDSA) to the list of known hosts.
发现磁盘错误:Host: z***7, RaidID_2, E_8~S_2, Media Error Count---20
该告警磁盘已处于故障状态!
主机列表共 15 台主机,巡检执行成功 15 台!

bug修复--无法准确识别在线的Error磁盘

修改:

  1. 增加一个Error输出文件,记录所有的告警和磁盘状态;
  2. 使用awk对Error输出文件进行处理,并进行排序和故障统计;
#!/bin/bash
# 输出文件定义函数
function checkTouchFile() {
    ResultFile="$(date +%Y%m%d-%H%M%S)-OsdDiskCheck.csv"
    ResultError="$(date +%Y%m%d-%H%M%S)-OsdDiskError.csv"
    if [ ! -f "$ResultFile" ] && [ ! -f "$ResultError" ]; then
        touch "$ResultFile"
        touch "$ResultError"
    else
        echo "$ResultFile$ResultError 巡检结果文件已存在,请检查是否需要删除!"
        break
    fi
}

function readHosts() {
    mapfile -t hosts < $1
}

# 磁盘巡检函数:
function diskScan() {
    failHost=()
    hostsLen="${#hosts[@]}"
    hostsSuc=0

    for line in "${hosts[@]}"; do
        echo "----------ssh $line----------"
        if ! diskOutPut=`ssh $line 'megacli -ldpdinfo -aALL -Nolog | egrep -i "Virtual Drive|RAID Level|^Size|^State|Enclosure Device|Slot Number|Device Id|media error|other error|^Predictive|Firmware stat|Temperature"' && sleep 3`
        then
            echo "$line 巡检失败,请检查"
            failHost+=(" $line")
        else
        hostsSuc=$((hostsSuc+1))
        while IFS=: read -r key value; do
            key=`echo $key | awk -F'  ' '{print $1}'`
            value="${value#"${value%%[![:space:]]*}"}"
            if [[ $key =~ "Virtual Drive" ]]; then
                keyVirt="RaidID_`echo $value|awk '{print $1}'`"
            elif [[ $key =~ "Size" ]]; then
                keyVol="$value"
            elif [[ $key =~ "Enclosure Device ID" ]]; then
                keyEID="E_$value"
            elif [[ $key =~ "Slot Number" ]]; then
                keySID="S_$value"
            elif [[ $key =~ "Count" ]]; then
                key="Host--$line, $keyVirt $keyVol, $keyEID~$keySID---$key"
                if [[ $value != 0 ]]; then
                    echo "$key:$value---DiskFault" >> "$ResultError"
                fi
            elif [[ $key =~ "Firmware state" ]]; then
                key="Host--$line, $keyVirt $keyVol, $keyEID~$keySID---$key"
                if [[ $value =~ "Online" ]]; then
                    echo "$key---DiskOnline"  >> "$ResultError"
                else
                    echo "$key---DiskOffline"  >> "$ResultError"
                fi
            else
                key="$key"
            fi
            echo "$key---$value" >> "$ResultFile"
        done <<<"$diskOutPut"
        fi
    done

    echo "主机列表共 $hostsLen 台主机,巡检执行成功 $hostsSuc 台!"
    if [ ${#failHost[@]} -ne 0 ]; then
        echo "执行失败的主机有:${failHost[@]}。"
    fi
}

# awk分析函数:
function analysis() {
    has_alert=0
    online_count=0

    awk -F '---' '
    {
        lines[$1] = $0;
        if ($NF == "DiskFault") {
            fault_lines[$1] = 1;
        }
    }
    END {
        for (line in fault_lines) {
            line_content = lines[line];
            split(line_content, fields, "---");
            last_field = fields[length(fields)];
            for (other_line in lines) {
                if (other_line == line) {
                    split(lines[other_line], other_fields, "---");
                    other_last_field = other_fields[length(other_fields)];
                    if (other_last_field == "DiskOnline") {
                        online_count++;
                        print "" line_content " 磁盘告警且仍然在线,请密切关注,确认是否手动踢盘!";
                        has_alert=1;
                    }
                }
            }
        }
        if (!has_alert) {
            print "当前没有在线的告警磁盘";
        }
        print "在线预故障磁盘数量统计: " online_count;
    }
    ' "$ResultError" | sort -k1,1
}

# 调用函数,完成脚本功能:
checkTouchFile
readHosts "osdServerList.txt"
diskScan
analysis
posted @   飞飞6779  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示