发现自己写的文章有点像是科技小品文,没有太多的技术含量,读起来也没太大难度,甚至可能像清水煮白菜,没什么味道。但是文章中的这些问题确实是在工作中碰到的,经过无数的google,或者向同事请教才解决的。自己经历的痛苦可能还会有人遇到,所以,为了”中国梦“的早日实现,无论如何,也要把这些问题还是记录下来,希望对他人能有所帮助。不积跬步无以至千里,相信总有一天猪也会飞起来的


这个问题我曾经在ChinaUnix上求助过,可能发的版面不对,几个月也没有人解答,后来老大帮忙求助了驱动部门的人,问题很快有了答案。


问题很简单:我们需要监控x86-64 Linux服务器的电源状态,看看两个电源是否都正常工作,如果有一个坏了,要发告警上来。


记得开始的时候自己查遍了Linux的操作文档,想找到一个命令或者从一个文件中能够直接把电源状态读出来,甚至把拔插电源前后的linux目录也比较了一遍,可是找了半天也没点线索,凭什么查CPU、内存都有命令,查个电源就没命令呢?后来发现这个可能真没有!

自己不懂驱动,更不懂硬件,拿到别人写的脚本,不能稀里糊涂地用,试图能搞得明白点。后面是自己的理解,错误之处还请海涵。

首先明白的一点是,这个方案也不是通用的,电源状态因硬件而异,不同服务器会有不同的方式。
我们用的硬件使用的是I2C总线... 什么?I2C是什么也不知道?那太好了,跟我一样,下面翻译点海外资料。
信息来源:http://www.esacademy.com/en/library/technical-articles-and-documents/miscellaneous/i2c-bus.html
I2C总线协议
I2C总线物理上由两根活性线和一根地线组成。活性线,分别叫做SDA和SCL,都是双向工作的。SDA是串行数据线,SCL是串行时钟线。
每一个连接到总线的设备都有它自己的地址,不管它是MCU,LCD驱动,内存,或者ASIC。每一个芯片既可以作为接受方,也可以作为发送方,或者二者兼具,这取决于它们的功能。很明显,LCD驱动只是一个接收方,而内存或者I/O芯片可以同时做发送方或者接收方。
I2C总线是一个multi-master的总线,也就是说不止一个能够发起数据传输命令的IC可以连接到它上面。I2C规范说,发起数据传输的IC就可以认为是总线的Master,其它的所有IC就被看作是总线的Slave。
由于bus master通常是微控制器,让我们来看一下一个常见的“IC间的对话”。考虑下面的设置,假设MCU想发送数据给一个slave。

服务器电源策略BIOS设置 服务器的电源_Power


首先,MCU发送一个START状态。这像是给所有连接的设备发送了一个“立正”的命令,总线上所有IC将监听即将到来的数据。

接着,MCU发送了一个它想访问的ADDRESS命令,同时带有是读还是写的信息(假设是写)。收到这个地址后,所有IC都会将之与自己的地址比对,如果不匹配的话,它们只是简单地等待直到发送STOP状态后总线被释放;然而如果是匹配的话,这个芯片就要发送一个ACKNOWLEDGE的信号来回应。

一旦MCU收到了ACKNOWLEDGE就开始收发数据。在我们的例子中,MCU将发送数据。这都做完以后,MCU发送一个STOP状态。这个信号表示总线被释放了,IC们可以等待下一个传输请求了。

我们的例子中包括了好几个指令:START,ADDRESS,ACKNOWLEDGE,DATA,STOP。这些在总线上都是独一无二的状态。在我们想进一步探究总线状态之前,我们需要了解一点物理结构和总线的硬件知识。

==============华丽丽的分割线==================
Linux的I2C工具以前是作为lm-sensors的一部分,现在已经拆分为独立的软件包。原因是并非所有硬件监控芯片都是I2C设备,I2C设备也不都是硬件监控芯片,所以把所有东西放在一起有些不太合理。因为I2C工具是在内核实现的,所以所有Linux版本都会支持。
我们的服务器可能有多条I2C总线,电源控制器连接在其中一条上。电源热拔插后,状态信息会在保存在电源控制器中,我们要做的就是通过I2C总线找到电源控制器的地址然后把数据读出来,这个工作由i2cget命令完成。
i2cget [-y] i2cbus chip-address [data-address [mode]]
-y去使能交互模式,使用脚本时需要加这个参数,避免确认信息。参数i2cbus表示需要扫描的I2C总线号,这个号必须在i2cdetect -l的列表当中; chip-address指定总线上那个芯片的地址,范围是0x03到0x77;data-address指定芯片上要读取数据的地址,范围是0x00到0xFF;mode如果指定的话表示读写模式,不带这个参数默认情况是只读模式。

脚本片段:

# Check for i2c-tools
if [ ! -f /usr/sbin/i2cget ] ; then
   echo -e "\nError: i2c-tools are required for this monitoring tool."
   exit
fi
# Check for lm_sensors
# Load i2c_dev modules if not loaded
if [ "`lsmod | grep -c i2c_dev`" == 0 ] ; then
        modprobe i2c_dev 2> /dev/null
fi

# Query for PS modules, 250w PS registered at i2c address 0x20
ps_status=`i2cget -y 0 0x20 0x00 2> /dev/null`

if [ "$ps_status" == "" ] ; then
      echo -e "\nError: PS module not responding, check i2c/lm_sensors setup."
      exit
fi
echo -e "\nSystem Power Supply Status:"
echo -e "---------------------------\n"
# Parse PS1 Bottom power supply module status
# Bit 0 = PS1 FAN status (0 = normal, 1 = fail)
let "ps_fan=$ps_status&1"
# Bit 1 = PS1 OTP (0 = normal, 1 = Over 55C, shuts off at 65C)
let "ps_otp=$ps_status&2"
# Bit 2 = PS1 PG (1 = Power good, 0 = Power good failed)
let "ps_pg=$ps_status&4"
# Bit 3 = PS1 Present (0 = PS detected on backplane, 1 = PS not present)
let "ps_present=$ps_status&8"

# Print PS1 results
if [ "$ps_present" == "0" ] ; then
        echo -en "PS1 BTM MODULE:\tPRESENT\nPS1 FAN STATUS:\t"
        if [ "$ps_fan" == "0" ] ; then echo "GOOD"
        else echo "FAILED"
        fi
        if [ "$ps_otp" == "0" ] ; then echo -e "PS1 OTP:\tGOOD (<55C)"
        else echo -e "PS1 OTP:\tFAILED (>55C)"
        fi
        if [ "$ps_pg" == "0" ] ; then echo -e "PS1 POWER:\tFAILED"
        else echo -e "PS1 POWER:\tGOOD"
        fi
else
        echo -e "PS1 BTM MODULE:\tNOT DETECTED"
fi
# Parse PS2 Top power supply module status
# Bit 4 = PS2 FAN status (0 = normal, 1 = fail)
let "ps_fan=$ps_status&16"
# Bit 5 = PS2 OTP (0 = normal, 1 = Over 55C, shuts off at 65C)
let "ps_otp=$ps_status&32"
# Bit 6 = PS2 PG (1 = Power good, 0 = Power good failed)
let "ps_pg=$ps_status&64"
# Bit 7 = PS2 Present (0 = PS detected on backplane, 1 = PS not present)
let "ps_present=$ps_status&128"

# Print PS2 results
if [ "$ps_present" == "0" ] ; then
        echo -en "\nPS2 TOP MODULE:\tPRESENT\nPS2 FAN STATUS:\t"
        if [ "$ps_fan" == "0" ] ; then echo "GOOD"
        else echo "FAILED"
        fi
        if [ "$ps_otp" == "0" ] ; then echo -e "PS2 OTP:\tGOOD (<55C)"
        else echo -e "PS2 OTP:\tFAILED (>55C)"
        fi
        if [ "$ps_pg" == "0" ] ; then echo -e "PS2 POWER:\tFAILED"
        else echo -e "PS2 POWER:\tGOOD"
        fi
else
        echo -e "\nPS2 TOP MODULE:\tNOT DETECTED"
fi


电源状态取出来了发个告警就简单了,不再赘述。