Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipmimonitor
import (
"encoding/hex"
"errors"
"strings"
"time"
"github.com/u-root/u-root/pkg/ipmi"
"ascend-common/common-utils/hwlog"
"nodeD/pkg/common"
"nodeD/pkg/common/manager"
)
var currentAlarmReqPrefix = []byte{0x30, 0x94, 0xDB, 0x07, 0x00, 0x40, 0x00}
var currentAlarmReqSuffix = []byte{0x0E, 0xFF}
const maxIpmiLoop = 100
type IpmiEventMonitor struct {
ipmiTool *ipmi.IPMI
faultManager manager.FaultManager
stopChan chan struct{}
}
func NewIpmiEventMonitor() *IpmiEventMonitor {
return &IpmiEventMonitor{
ipmiTool: &ipmi.IPMI{},
faultManager: manager.NewFaultManager(),
stopChan: make(chan struct{}, 1),
}
}
func (i *IpmiEventMonitor) Monitoring() {
for {
select {
case _, ok := <-i.stopChan:
if !ok {
hwlog.RunLog.Error("stop channel is closed")
return
}
hwlog.RunLog.Info("receive stop signal, ipmi monitor shut down...")
return
default:
time.Sleep(time.Duration(common.ParamOption.MonitorPeriod) * time.Second)
if err := i.UpdateFaultDevList(); err != nil {
hwlog.RunLog.Errorf("ipmi monitor update fault device list failed, error: %v", err)
}
}
}
}
func (i *IpmiEventMonitor) Init() error {
ipmiTool, err := ipmi.Open(0)
if err != nil {
hwlog.RunLog.Errorf("open ipmi device failed, error: %v", err)
return err
}
i.ipmiTool = ipmiTool
if err := i.UpdateFaultDevList(); err != nil {
hwlog.RunLog.Errorf("ipmi monitor update fault device list failed, please check bmc version and server type, "+
"error: %v", err)
return err
}
return nil
}
func (i *IpmiEventMonitor) Stop() {
if err := i.ipmiTool.Close(); err != nil {
hwlog.RunLog.Errorf("ipmi tool close device failed, err is %v", err)
}
i.stopChan <- struct{}{}
}
func (i *IpmiEventMonitor) Name() string {
return common.PluginMonitorIpmi
}
func (i *IpmiEventMonitor) GetMonitorData() *common.FaultAndConfigInfo {
fcInfo := &common.FaultAndConfigInfo{
FaultDevInfo: i.faultManager.GetFaultDevInfo(),
}
return fcInfo
}
func (i *IpmiEventMonitor) UpdateFaultDevList() error {
currentAlarmFaultEvents, err := i.GetCurrentAlarmFaultEvents()
if err != nil {
hwlog.RunLog.Errorf("get current alarm fault events failed, error: %v", err)
return errors.New("get current alarm fault events failed")
}
printFaultEvents(currentAlarmFaultEvents)
i.faultManager.SetFaultDevList(GetFaultDevList(currentAlarmFaultEvents))
common.TriggerUpdate(common.IpmiProcess)
return nil
}
func (i *IpmiEventMonitor) GetCurrentAlarmFaultEvents() ([]*common.FaultEvent, error) {
var alarmEvents []*common.FaultEvent
var eventIndex int64 = 0
firstAlarmMsg, err := i.ipmiTool.RawCmd(GetCurrentAlarmReq(eventIndex))
if err != nil || len(firstAlarmMsg) < common.EventFieldStartIndex {
hwlog.RunLog.Errorf("get first alarm msg from ipmi failed, error: %v", err)
return nil, errors.New("get first alarm msg from ipmi failed")
}
alarmEvents = append(alarmEvents, GetFaultEvents(firstAlarmMsg[common.EventFieldStartIndex:])...)
totalNumEvents := GetTotalEventsNum(firstAlarmMsg[common.TotalEventsStartIndex:common.TotalEventsEndIndex])
msgNumEvents := int64(firstAlarmMsg[common.MsgEventsIndex])
eventIndex += msgNumEvents
totalNumEvents -= msgNumEvents
currentIpmiLoop := 0
for totalNumEvents > 0 && currentIpmiLoop < maxIpmiLoop {
currentIpmiLoop++
if eventIndex < 0 || eventIndex > common.MaxSixTeenBitIntValue {
break
}
nextAlarmMsg, err := i.ipmiTool.RawCmd(GetCurrentAlarmReq(eventIndex))
if err != nil || len(nextAlarmMsg) < common.EventFieldStartIndex {
hwlog.RunLog.Errorf("get another alarm msg from ipmi failed, error: %v", err)
return nil, errors.New("get another alarm msg from ipmi failed")
}
msgNumEvents = int64(nextAlarmMsg[common.MsgEventsIndex])
eventIndex += msgNumEvents
totalNumEvents -= msgNumEvents
alarmEvents = append(alarmEvents, GetFaultEvents(nextAlarmMsg[common.EventFieldStartIndex:])...)
}
return alarmEvents, nil
}
func GetCurrentAlarmReq(eventIndex int64) []byte {
return append(append(currentAlarmReqPrefix, common.ConvertIntToTwoByteSlice(eventIndex)...),
currentAlarmReqSuffix...)
}
func GetFaultDevList(faultEvents []*common.FaultEvent) []*common.FaultDev {
faultDevErrCodeMap := make(map[string]map[int64][]string, 0)
for _, faultEvent := range faultEvents {
if _, ok := faultDevErrCodeMap[faultEvent.DeviceType]; !ok {
faultDevErrCodeMap[faultEvent.DeviceType] = make(map[int64][]string, 0)
}
faultDevMap := faultDevErrCodeMap[faultEvent.DeviceType]
if _, ok := faultDevMap[faultEvent.DeviceId]; !ok {
faultDevMap[faultEvent.DeviceId] = make([]string, 0)
}
faultDevMap[faultEvent.DeviceId] = append(faultDevMap[faultEvent.DeviceId], faultEvent.ErrorCode)
}
var faultDevList []*common.FaultDev
for deviceType, deviceMap := range faultDevErrCodeMap {
for deviceId, errorCodeList := range deviceMap {
faultDevList = append(faultDevList, &common.FaultDev{
DeviceType: deviceType,
DeviceId: deviceId,
FaultCode: common.RemoveDuplicateString(errorCodeList),
})
}
}
return faultDevList
}
func GetTotalEventsNum(totalEventNumByte []byte) int64 {
if len(totalEventNumByte) != common.TotalEventsByteLength {
hwlog.RunLog.Error("input wrong total event num byte!")
return 0
}
return int64(totalEventNumByte[1])*common.HexByteBase + int64(totalEventNumByte[0])
}
func GetFaultEvents(eventsByte []byte) []*common.FaultEvent {
if len(eventsByte)%common.SingleEventBytes != 0 {
hwlog.RunLog.Errorf("events byte length is illegal, length is %d", len(eventsByte))
return nil
}
var faultEvents []*common.FaultEvent
for i := 0; i < len(eventsByte)-1; i += common.SingleEventBytes {
faultEvents = append(faultEvents, GetFaultEvent(eventsByte[i:i+common.SingleEventBytes]))
}
return faultEvents
}
func GetFaultEvent(eventByte []byte) *common.FaultEvent {
if len(eventByte) <= common.DeviceIdIndex {
return nil
}
return &common.FaultEvent{
ErrorCode: strings.ToUpper(hex.EncodeToString(
common.RevertByteSlice(eventByte[common.ErrorCodeStartIndex:common.ErrorCodeEndIndex]))),
Severity: int64(eventByte[common.SeverityIndex]),
DeviceType: common.GetDeviceType(eventByte[common.DeviceTypeIndex]),
DeviceId: int64(eventByte[common.DeviceIdIndex]),
}
}
func printFaultEvents(faultEvents []*common.FaultEvent) {
for _, faultEvent := range faultEvents {
hwlog.RunLog.Infof("get fault event, [device type: %s]-[device id: %d]-[error code: %s]",
faultEvent.DeviceType, faultEvent.DeviceId, faultEvent.ErrorCode)
}
}