raiddetails.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package raid
  2. import (
  3. "fmt"
  4. "os/exec"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. // RAIDInfo represents information about a RAID array.
  10. type RAIDInfo struct {
  11. DevicePath string
  12. Version string
  13. CreationTime time.Time
  14. RaidLevel string
  15. ArraySize int
  16. UsedDevSize int
  17. RaidDevices int
  18. TotalDevices int
  19. Persistence string
  20. UpdateTime time.Time
  21. State string
  22. ActiveDevices int
  23. WorkingDevices int
  24. FailedDevices int
  25. SpareDevices int
  26. Consistency string
  27. Name string
  28. UUID string
  29. Events int
  30. DeviceInfo []DeviceInfo
  31. }
  32. // DeviceInfo represents information about a device in a RAID array.
  33. type DeviceInfo struct {
  34. State []string
  35. DevicePath string
  36. RaidDevice int //Sequence of the raid device?
  37. }
  38. // GetRAIDInfo retrieves information about a RAID array using the mdadm command.
  39. // arrayName must be in full path (e.g. /dev/md0)
  40. func (m *Manager) GetRAIDInfo(arrayName string) (*RAIDInfo, error) {
  41. cmd := exec.Command("sudo", "mdadm", "--detail", arrayName)
  42. output, err := cmd.Output()
  43. if err != nil {
  44. return nil, fmt.Errorf("error running mdadm command: %v", err)
  45. }
  46. info := parseRAIDInfo(string(output))
  47. //Fill in the device path so other service can use it more easily
  48. info.DevicePath = arrayName
  49. return info, nil
  50. }
  51. // parseRAIDInfo parses the output of mdadm --detail command and returns the RAIDInfo struct.
  52. func parseRAIDInfo(output string) *RAIDInfo {
  53. lines := strings.Split(output, "\n")
  54. raidInfo := &RAIDInfo{}
  55. for _, line := range lines {
  56. fields := strings.Fields(line)
  57. if len(fields) >= 2 {
  58. switch fields[0] {
  59. case "Version":
  60. raidInfo.Version = fields[2]
  61. case "Creation":
  62. creationTimeStr := strings.Join(fields[3:], " ")
  63. creationTime, _ := time.Parse(time.ANSIC, creationTimeStr)
  64. raidInfo.CreationTime = creationTime
  65. case "Raid":
  66. if fields[1] == "Level" {
  67. //Raid Level
  68. raidInfo.RaidLevel = fields[3]
  69. } else if fields[1] == "Devices" {
  70. raidInfo.RaidDevices, _ = strconv.Atoi(fields[3])
  71. }
  72. case "Array":
  73. raidInfo.ArraySize, _ = strconv.Atoi(fields[3])
  74. case "Used":
  75. raidInfo.UsedDevSize, _ = strconv.Atoi(fields[4])
  76. case "Total":
  77. raidInfo.TotalDevices, _ = strconv.Atoi(fields[3])
  78. case "Persistence":
  79. raidInfo.Persistence = strings.Join(fields[2:], " ")
  80. case "Update":
  81. updateTimeStr := strings.Join(fields[3:], " ")
  82. updateTime, _ := time.Parse(time.ANSIC, updateTimeStr)
  83. raidInfo.UpdateTime = updateTime
  84. case "State":
  85. raidInfo.State = strings.Join(fields[2:], " ")
  86. case "Active":
  87. raidInfo.ActiveDevices, _ = strconv.Atoi(fields[3])
  88. case "Working":
  89. raidInfo.WorkingDevices, _ = strconv.Atoi(fields[3])
  90. case "Failed":
  91. raidInfo.FailedDevices, _ = strconv.Atoi(fields[3])
  92. case "Spare":
  93. raidInfo.SpareDevices, _ = strconv.Atoi(fields[3])
  94. case "Consistency":
  95. raidInfo.Consistency = strings.Join(fields[3:], " ")
  96. case "Name":
  97. raidInfo.Name = strings.Join(fields[2:], " ")
  98. case "UUID":
  99. raidInfo.UUID = fields[2]
  100. case "Events":
  101. raidInfo.Events, _ = strconv.Atoi(fields[2])
  102. default:
  103. if len(fields) >= 5 && fields[0] != "Number" {
  104. deviceInfo := DeviceInfo{}
  105. if len(fields) > 3 {
  106. rdNo, err := strconv.Atoi(fields[3])
  107. if err != nil {
  108. rdNo = -1
  109. }
  110. deviceInfo.RaidDevice = rdNo
  111. }
  112. if len(fields) > 5 {
  113. //Only active disks have fields > 5, e.g.
  114. // 0 8 16 0 active sync /dev/sdb
  115. deviceInfo.State = fields[4 : len(fields)-1]
  116. deviceInfo.DevicePath = fields[len(fields)-1]
  117. } else {
  118. //Failed disk, e.g.
  119. // - 0 0 1 removed
  120. deviceInfo.State = fields[4:]
  121. //TODO: Add custom tags
  122. }
  123. raidInfo.DeviceInfo = append(raidInfo.DeviceInfo, deviceInfo)
  124. }
  125. }
  126. }
  127. }
  128. return raidInfo
  129. }
  130. // PrettyPrintRAIDInfo pretty prints the RAIDInfo struct.
  131. func (info *RAIDInfo) PrettyPrintRAIDInfo() {
  132. fmt.Println("RAID Array Information:")
  133. fmt.Printf(" Version: %s\n", info.Version)
  134. fmt.Printf(" Creation Time: %s\n", info.CreationTime.Format("Mon Jan 02 15:04:05 2006"))
  135. fmt.Printf(" Raid Level: %s\n", info.RaidLevel)
  136. fmt.Printf(" Array Size: %d\n", info.ArraySize)
  137. fmt.Printf(" Used Dev Size: %d\n", info.UsedDevSize)
  138. fmt.Printf(" Raid Devices: %d\n", info.RaidDevices)
  139. fmt.Printf(" Total Devices: %d\n", info.TotalDevices)
  140. fmt.Printf(" Persistence: %s\n", info.Persistence)
  141. fmt.Printf(" Update Time: %s\n", info.UpdateTime.Format("Mon Jan 02 15:04:05 2006"))
  142. fmt.Printf(" State: %s\n", info.State)
  143. fmt.Printf(" Active Devices: %d\n", info.ActiveDevices)
  144. fmt.Printf(" Working Devices: %d\n", info.WorkingDevices)
  145. fmt.Printf(" Failed Devices: %d\n", info.FailedDevices)
  146. fmt.Printf(" Spare Devices: %d\n", info.SpareDevices)
  147. fmt.Printf(" Consistency Policy: %s\n", info.Consistency)
  148. fmt.Printf(" Name: %s\n", info.Name)
  149. fmt.Printf(" UUID: %s\n", info.UUID)
  150. fmt.Printf(" Events: %d\n", info.Events)
  151. fmt.Println("\nDevice Information:")
  152. fmt.Printf("%s %s\n", "State", "DevicePath")
  153. for _, device := range info.DeviceInfo {
  154. fmt.Printf("%s %s\n", strings.Join(device.State, ","), device.DevicePath)
  155. }
  156. }