mdadmConf.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package raid
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "os/exec"
  7. "strings"
  8. "time"
  9. "imuslab.com/arozos/mod/disk/diskfs"
  10. "imuslab.com/arozos/mod/utils"
  11. )
  12. /*
  13. mdadmConf.go
  14. This package handles the config modification and update for
  15. the mdadm module
  16. */
  17. // Force mdadm to stop all RAID and load fresh from config file
  18. // on some Linux distro this is required as mdadm start too early
  19. func (m *Manager) FlushReload() error {
  20. //Get a list of currently running RAID devices
  21. raidDevices, err := m.GetRAIDDevicesFromProcMDStat()
  22. if err != nil {
  23. return err
  24. }
  25. //Stop all of the running RAID devices
  26. for _, rd := range raidDevices {
  27. //Check if it is mounted. If yes, skip this
  28. devMounted, err := diskfs.DeviceIsMounted("/dev/" + rd.Name)
  29. if devMounted || err != nil {
  30. log.Println("[RAID] " + "/dev/" + rd.Name + " is in use. Skipping.")
  31. continue
  32. }
  33. log.Println("[RAID] Stopping " + rd.Name)
  34. cmdMdadm := exec.Command("sudo", "mdadm", "--stop", "/dev/"+rd.Name)
  35. // Run the command and capture its output
  36. _, err = cmdMdadm.Output()
  37. if err != nil {
  38. log.Println("[RAID] Unable to stop " + rd.Name + ". Skipping")
  39. continue
  40. }
  41. }
  42. time.Sleep(300 * time.Millisecond)
  43. //Assemble mdadm array again
  44. err = m.RestartRAIDService()
  45. if err != nil {
  46. return err
  47. }
  48. return nil
  49. }
  50. // Updates the mdadm configuration file with the details of RAID arrays
  51. // so the RAID drive will still be seen after a reboot (hopefully)
  52. // this will automatically add / remove config base on current runtime setup
  53. func (m *Manager) UpdateMDADMConfig() error {
  54. cmdMdadm := exec.Command("sudo", "mdadm", "--detail", "--scan", "--verbose")
  55. // Run the command and capture its output
  56. output, err := cmdMdadm.Output()
  57. if err != nil {
  58. return fmt.Errorf("error running mdadm command: %v", err)
  59. }
  60. //Load the config from system
  61. currentConfigBytes, err := os.ReadFile("/etc/mdadm/mdadm.conf")
  62. if err != nil {
  63. return fmt.Errorf("unable to open mdadm.conf: " + err.Error())
  64. }
  65. currentConf := string(currentConfigBytes)
  66. //Check if the current config already contains the setting
  67. newConfigLines := []string{}
  68. uuidsInNewConfig := []string{}
  69. arrayConfigs := strings.TrimSpace(string(output))
  70. lines := strings.Split(arrayConfigs, "ARRAY")
  71. for _, line := range lines {
  72. //For each line, you should have something like this
  73. //ARRAY /dev/md0 metadata=1.2 name=debian:0 UUID=cbc11a2b:fbd42653:99c1340b:9c4962fb
  74. // devices=/dev/sdb,/dev/sdc
  75. //Building structure for RAID Config Record
  76. line = strings.ReplaceAll(line, "\n", " ")
  77. fields := strings.Fields(line)
  78. if len(fields) < 5 {
  79. continue
  80. }
  81. poolUUID := strings.TrimPrefix(fields[3], "UUID=")
  82. uuidsInNewConfig = append(uuidsInNewConfig, poolUUID)
  83. //Check if this uuid already in the config file
  84. if strings.Contains(currentConf, poolUUID) {
  85. continue
  86. }
  87. //This config not exists in the settings. Add it to append lines
  88. log.Println("[RAID] Adding " + fields[0] + " (UUID=" + poolUUID + ") into mdadm config")
  89. settingLine := "ARRAY " + strings.Join(fields, " ")
  90. newConfigLines = append(newConfigLines, settingLine)
  91. }
  92. originalConfigLines := strings.Split(strings.TrimSpace(currentConf), "\n")
  93. poolUUIDToBeRemoved := []string{}
  94. for _, line := range originalConfigLines {
  95. lineFields := strings.Fields(line)
  96. for _, thisField := range lineFields {
  97. if strings.HasPrefix(thisField, "UUID=") {
  98. //This is the UUID of this array. Check if it still exists in new storage config
  99. thisPoolUUID := strings.TrimPrefix(thisField, "UUID=")
  100. existsInNewConfig := utils.StringInArray(uuidsInNewConfig, thisPoolUUID)
  101. if !existsInNewConfig {
  102. //Label this UUID to be removed
  103. poolUUIDToBeRemoved = append(poolUUIDToBeRemoved, thisPoolUUID)
  104. }
  105. //Skip scanning the remaining fields of this RAID pool
  106. break
  107. }
  108. }
  109. }
  110. if len(poolUUIDToBeRemoved) > 0 {
  111. //Remove the old UUIDs from config
  112. for _, volumeUUID := range poolUUIDToBeRemoved {
  113. err = m.RemoveVolumeFromMDADMConfig(volumeUUID)
  114. if err != nil {
  115. log.Println("[RAID] Error when trying to remove old RAID volume from config: " + err.Error())
  116. return err
  117. } else {
  118. log.Println("[RAID] RAID volume " + volumeUUID + " removed from config file")
  119. }
  120. }
  121. }
  122. if len(newConfigLines) == 0 {
  123. //Nothing to write
  124. log.Println("[RAID] Nothing to write. Skipping mdadm config update.")
  125. return nil
  126. }
  127. // Construct the bash command to append the line to mdadm.conf using echo and tee
  128. for _, configLine := range newConfigLines {
  129. cmd := exec.Command("bash", "-c", fmt.Sprintf(`echo "%s" | sudo tee -a /etc/mdadm/mdadm.conf`, configLine))
  130. // Run the command
  131. err := cmd.Run()
  132. if err != nil {
  133. return fmt.Errorf("error injecting line into mdadm.conf: %v", err)
  134. }
  135. }
  136. return nil
  137. }
  138. // Removes a RAID volume from the mdadm configuration file given its volume UUID.
  139. // Note that this only remove a single line of config. If your line consists of multiple lines
  140. // you might need to remove it manually
  141. func (m *Manager) RemoveVolumeFromMDADMConfig(volumeUUID string) error {
  142. // Construct the sed command to remove the line containing the volume UUID from mdadm.conf
  143. sedCommand := fmt.Sprintf(`sudo sed -i '/UUID=%s/d' /etc/mdadm/mdadm.conf`, volumeUUID)
  144. // Execute the sed command
  145. cmd := exec.Command("bash", "-c", sedCommand)
  146. err := cmd.Run()
  147. if err != nil {
  148. return fmt.Errorf("error removing volume from mdadm.conf: %v", err)
  149. }
  150. return nil
  151. }