raidutils.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package raid
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "strings"
  10. )
  11. // Get the next avaible RAID array name
  12. func GetNextAvailableMDDevice() (string, error) {
  13. for i := 0; i < 100; i++ {
  14. mdDevice := fmt.Sprintf("/dev/md%d", i)
  15. if _, err := os.Stat(mdDevice); os.IsNotExist(err) {
  16. return mdDevice, nil
  17. }
  18. }
  19. return "", fmt.Errorf("no available /dev/mdX devices found")
  20. }
  21. // DANGER: Wipe the whole disk given the disk path
  22. func (m *Manager) WipeDisk(diskPath string) error {
  23. // Unmount the disk
  24. isMounted, _ := DeviceIsMounted(diskPath)
  25. if isMounted {
  26. umountCmd := exec.Command("sudo", "umount", diskPath)
  27. if err := umountCmd.Run(); err != nil {
  28. return fmt.Errorf("error unmounting disk %s: %v", diskPath, err)
  29. }
  30. }
  31. // Wipe all filesystem signatures on the entire disk
  32. wipeCmd := exec.Command("sudo", "wipefs", "--all", "--force", diskPath)
  33. if err := wipeCmd.Run(); err != nil {
  34. return fmt.Errorf("error wiping filesystem signatures on %s: %v", diskPath, err)
  35. }
  36. return nil
  37. }
  38. // ClearSuperblock clears the superblock of the specified disk so it can be used safely
  39. func (m *Manager) ClearSuperblock(devicePath string) error {
  40. isMounted, err := DeviceIsMounted(devicePath)
  41. if err != nil {
  42. return errors.New("unable to validate if the device is unmounted: " + err.Error())
  43. }
  44. if isMounted {
  45. return errors.New("target device is mounted. Make sure it is unmounted before clearing")
  46. }
  47. cmd := exec.Command("sudo", "mdadm", "--zero-superblock", devicePath)
  48. err = cmd.Run()
  49. if err != nil {
  50. return fmt.Errorf("error clearing superblock: %v", err)
  51. }
  52. return nil
  53. }
  54. // Check if a device is mounted given the path name, like /dev/sdc
  55. func DeviceIsMounted(devicePath string) (bool, error) {
  56. // Open the mountinfo file
  57. file, err := os.Open("/proc/mounts")
  58. if err != nil {
  59. return false, fmt.Errorf("error opening /proc/mounts: %v", err)
  60. }
  61. defer file.Close()
  62. // Scan the mountinfo file line by line
  63. scanner := bufio.NewScanner(file)
  64. for scanner.Scan() {
  65. line := scanner.Text()
  66. fields := strings.Fields(line)
  67. if len(fields) >= 2 && fields[0] == devicePath {
  68. // Device is mounted
  69. return true, nil
  70. }
  71. }
  72. // Device is not mounted
  73. return false, nil
  74. }
  75. // Use to restart any not-removed RAID device
  76. func (m *Manager) RestartRAIDService() error {
  77. cmd := exec.Command("sudo", "mdadm", "--assemble", "--scan")
  78. // Run the command
  79. output, err := cmd.CombinedOutput()
  80. if err != nil {
  81. if string(output) == "" {
  82. //Nothing updated in config.
  83. return nil
  84. }
  85. return fmt.Errorf("error restarting RAID device: %s", strings.TrimSpace(string(output)))
  86. }
  87. return nil
  88. }
  89. // Stop RAID device with given path
  90. func (m *Manager) StopRAIDDevice(devicePath string) error {
  91. cmd := exec.Command("sudo", "mdadm", "--stop", devicePath)
  92. // Run the command
  93. err := cmd.Run()
  94. if err != nil {
  95. return fmt.Errorf("error stopping RAID device: %v", err)
  96. }
  97. return nil
  98. }
  99. // RemoveRAIDDevice removes the specified RAID device member (disk).
  100. func (m *Manager) RemoveRAIDMember(devicePath string) error {
  101. // Construct the mdadm command to remove the RAID device
  102. cmd := exec.Command("sudo", "mdadm", "--remove", devicePath)
  103. // Run the command
  104. output, err := cmd.CombinedOutput()
  105. if err != nil {
  106. // If there was an error, return the combined output and the error message
  107. return fmt.Errorf("error removing RAID device: %s", strings.TrimSpace(string(output)))
  108. }
  109. return nil
  110. }
  111. // UnmountDevice unmounts the specified device.
  112. // Remember to use full path (e.g. /dev/md0) in the devicePath
  113. func UnmountDevice(devicePath string) error {
  114. // Construct the bash command to unmount the device
  115. cmd := exec.Command("sudo", "bash", "-c", fmt.Sprintf("umount %s", devicePath))
  116. // Run the command
  117. err := cmd.Run()
  118. if err != nil {
  119. return fmt.Errorf("error unmounting device: %v", err)
  120. }
  121. return nil
  122. }
  123. // IsValidRAIDLevel checks if the given RAID level is valid.
  124. func IsValidRAIDLevel(level string) bool {
  125. // List of valid RAID levels
  126. validLevels := []string{"raid1", "raid0", "raid6", "raid5", "raid4", "raid10"}
  127. // Convert the RAID level to lowercase and remove any surrounding whitespace
  128. level = strings.TrimSpace(strings.ToLower(level))
  129. // Check if the level exists in the list of valid levels
  130. for _, validLevel := range validLevels {
  131. if level == validLevel {
  132. return true
  133. }
  134. }
  135. // Return false if the level is not found in the list of valid levels
  136. return false
  137. }
  138. // Get RAID device info from device path
  139. func (m *Manager) GetRAIDDeviceByDevicePath(devicePath string) (*RAIDDevice, error) {
  140. //Strip the /dev/ part if it was accidentally passed in
  141. devicePath = filepath.Base(devicePath)
  142. //Get all the raid devices
  143. rdevs, err := m.GetRAIDDevicesFromProcMDStat()
  144. if err != nil {
  145. return nil, err
  146. }
  147. //Check for match
  148. for _, rdev := range rdevs {
  149. if rdev.Name == devicePath {
  150. return &rdev, nil
  151. }
  152. }
  153. return nil, errors.New("target RAID device not found")
  154. }
  155. // Check if a RAID device exists, e.g. md0
  156. func (m *Manager) RAIDDeviceExists(devicePath string) bool {
  157. _, err := m.GetRAIDDeviceByDevicePath(devicePath)
  158. return err == nil
  159. }