mdadm.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package raid
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "regexp"
  7. "strconv"
  8. "strings"
  9. )
  10. /*
  11. mdadm manager
  12. This script handles the interaction with mdadm
  13. */
  14. type StorageDeviceMeta struct {
  15. Name string
  16. Size int64
  17. RO bool
  18. DevType string
  19. MountPoint string
  20. }
  21. // List all the storage device in the system, set minSize to 0 for no filter
  22. func (m *Manager) ListAllStorageDevices(minSize int64) ([]*StorageDeviceMeta, error) {
  23. cmd := exec.Command("sudo", "lsblk", "-b")
  24. output, err := cmd.CombinedOutput()
  25. if err != nil {
  26. return nil, fmt.Errorf("lsblk error: %v", err)
  27. }
  28. // Split the output into lines
  29. lines := strings.Split(string(output), "\n")
  30. var devices []*StorageDeviceMeta
  31. // Parse each line to extract device information
  32. for _, line := range lines[1:] { // Skip the header line
  33. fields := strings.Fields(line)
  34. if len(fields) < 7 {
  35. continue
  36. }
  37. size, err := strconv.ParseInt(fields[3], 10, 64)
  38. if err != nil {
  39. return nil, fmt.Errorf("error parsing device size: %v", err)
  40. }
  41. ro := fields[4] == "1"
  42. device := &StorageDeviceMeta{
  43. Name: fields[0],
  44. Size: size,
  45. RO: ro,
  46. DevType: fields[5],
  47. MountPoint: fields[6],
  48. }
  49. // Filter devices based on minimum size
  50. if size >= minSize {
  51. devices = append(devices, device)
  52. }
  53. }
  54. return devices, nil
  55. }
  56. // Return the uuid of the disk by its path name (e.g. /dev/sda)
  57. func (m *Manager) GetDiskUUIDByPath(devicePath string) (string, error) {
  58. cmd := exec.Command("sudo", "blkid", devicePath)
  59. output, err := cmd.CombinedOutput()
  60. if err != nil {
  61. return "", fmt.Errorf("blkid error: %v", err)
  62. }
  63. // Parse the output to extract the UUID
  64. fields := strings.Fields(string(output))
  65. for _, field := range fields {
  66. if strings.HasPrefix(field, "UUID=") {
  67. uuid := strings.TrimPrefix(field, "UUID=\"")
  68. uuid = strings.TrimSuffix(uuid, "\"")
  69. return uuid, nil
  70. }
  71. }
  72. return "", fmt.Errorf("UUID not found for device %s", devicePath)
  73. }
  74. // CreateRAIDDevice creates a RAID device using the mdadm command.
  75. func (m *Manager) CreateRAIDDevice(devName string, raidName string, raidLevel int, raidDeviceIds []string, spareDeviceIds []string) error {
  76. //Calculate the size of the raid devices
  77. raidDev := len(raidDeviceIds)
  78. spareDevice := len(spareDeviceIds)
  79. //Validate the number of disk is enough for the raid
  80. if raidLevel == 0 && raidDev < 2 {
  81. return fmt.Errorf("not enough disks for raid0")
  82. } else if raidLevel == 1 && raidDev < 2 {
  83. return fmt.Errorf("not enough disks for raid1")
  84. } else if raidLevel == 5 && raidDev < 3 {
  85. return fmt.Errorf("not enough disk for raid5")
  86. } else if raidLevel == 6 && raidDev < 4 {
  87. return fmt.Errorf("not enough disk for raid6")
  88. }
  89. // Concatenate RAID and spare device arrays
  90. allDeviceIds := append(raidDeviceIds, spareDeviceIds...)
  91. // Build the mdadm command
  92. cmd := exec.Command("bash", "-c", fmt.Sprintf("yes | sudo mdadm --create %s --name %s --level=%d --raid-devices=%d --spare-devices=%d %s",
  93. devName, raidName, raidLevel, raidDev, spareDevice, strings.Join(allDeviceIds, " ")))
  94. cmd.Stdout = os.Stdout
  95. cmd.Stderr = os.Stderr
  96. err := cmd.Run()
  97. if err != nil {
  98. return fmt.Errorf("error running mdadm command: %v", err)
  99. }
  100. return nil
  101. }
  102. // Updates the mdadm configuration file with the details of RAID arrays
  103. // so the RAID drive will still be seen after a reboot (hopefully)
  104. func UpdateMDADMConfig() error {
  105. cmdMdadm := exec.Command("sudo", "mdadm", "--detail", "--scan", "--verbose")
  106. // Run the command and capture its output
  107. output, err := cmdMdadm.Output()
  108. if err != nil {
  109. return fmt.Errorf("error running mdadm command: %v", err)
  110. }
  111. arrayConfigs := strings.TrimSpace(string(output))
  112. lines := strings.Split(arrayConfigs, "\n")
  113. //TODO: Add line filter for 2nd line appending
  114. for _, line := range lines {
  115. //For each line, you should have something like this
  116. //ARRAY /dev/md0 metadata=1.2 name=debian:0 UUID=cbc11a2b:fbd42653:99c1340b:9c4962fb
  117. // devices=/dev/sdb,/dev/sdc
  118. fields := strings.Fields(line)
  119. }
  120. //Extract the UUID from the output
  121. // Define a regular expression to match the UUID pattern
  122. uuidRegex := regexp.MustCompile(`UUID=([0-9a-fA-F:]+)`)
  123. // Find the first match of the UUID pattern in the output string
  124. matches := uuidRegex.FindStringSubmatch(output)
  125. if len(matches) < 2 {
  126. return "", fmt.Errorf("UUID not found in mdadm output")
  127. }
  128. // Extract the UUID from the matched string
  129. uuid := matches[1]
  130. }