package raid import ( "bufio" "errors" "fmt" "os" "os/exec" "path/filepath" "strings" ) // Get the next avaible RAID array name func GetNextAvailableMDDevice() (string, error) { for i := 0; i < 100; i++ { mdDevice := fmt.Sprintf("/dev/md%d", i) if _, err := os.Stat(mdDevice); os.IsNotExist(err) { return mdDevice, nil } } return "", fmt.Errorf("no available /dev/mdX devices found") } // DANGER: Wipe the whole disk given the disk path func (m *Manager) WipeDisk(diskPath string) error { // Unmount the disk isMounted, _ := DeviceIsMounted(diskPath) if isMounted { umountCmd := exec.Command("sudo", "umount", diskPath) if err := umountCmd.Run(); err != nil { return fmt.Errorf("error unmounting disk %s: %v", diskPath, err) } } // Wipe all filesystem signatures on the entire disk wipeCmd := exec.Command("sudo", "wipefs", "--all", "--force", diskPath) if err := wipeCmd.Run(); err != nil { return fmt.Errorf("error wiping filesystem signatures on %s: %v", diskPath, err) } return nil } // ClearSuperblock clears the superblock of the specified disk so it can be used safely func (m *Manager) ClearSuperblock(devicePath string) error { isMounted, err := DeviceIsMounted(devicePath) if err != nil { return errors.New("unable to validate if the device is unmounted: " + err.Error()) } if isMounted { return errors.New("target device is mounted. Make sure it is unmounted before clearing") } cmd := exec.Command("sudo", "mdadm", "--zero-superblock", devicePath) err = cmd.Run() if err != nil { return fmt.Errorf("error clearing superblock: %v", err) } return nil } // Check if a device is mounted given the path name, like /dev/sdc func DeviceIsMounted(devicePath string) (bool, error) { // Open the mountinfo file file, err := os.Open("/proc/mounts") if err != nil { return false, fmt.Errorf("error opening /proc/mounts: %v", err) } defer file.Close() // Scan the mountinfo file line by line scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() fields := strings.Fields(line) if len(fields) >= 2 && fields[0] == devicePath { // Device is mounted return true, nil } } // Device is not mounted return false, nil } // Use to restart any not-removed RAID device func (m *Manager) RestartRAIDService() error { cmd := exec.Command("sudo", "mdadm", "--assemble", "--scan") // Run the command output, err := cmd.CombinedOutput() if err != nil { if string(output) == "" { //Nothing updated in config. return nil } return fmt.Errorf("error restarting RAID device: %s", strings.TrimSpace(string(output))) } return nil } // Stop RAID device with given path func (m *Manager) StopRAIDDevice(devicePath string) error { cmd := exec.Command("sudo", "mdadm", "--stop", devicePath) // Run the command err := cmd.Run() if err != nil { return fmt.Errorf("error stopping RAID device: %v", err) } return nil } // RemoveRAIDDevice removes the specified RAID device member (disk). func (m *Manager) RemoveRAIDMember(devicePath string) error { // Construct the mdadm command to remove the RAID device cmd := exec.Command("sudo", "mdadm", "--remove", devicePath) // Run the command output, err := cmd.CombinedOutput() if err != nil { // If there was an error, return the combined output and the error message return fmt.Errorf("error removing RAID device: %s", strings.TrimSpace(string(output))) } return nil } // UnmountDevice unmounts the specified device. // Remember to use full path (e.g. /dev/md0) in the devicePath func UnmountDevice(devicePath string) error { // Construct the bash command to unmount the device cmd := exec.Command("sudo", "bash", "-c", fmt.Sprintf("umount %s", devicePath)) // Run the command err := cmd.Run() if err != nil { return fmt.Errorf("error unmounting device: %v", err) } return nil } // IsValidRAIDLevel checks if the given RAID level is valid. func IsValidRAIDLevel(level string) bool { // List of valid RAID levels validLevels := []string{"raid1", "raid0", "raid6", "raid5", "raid4", "raid10"} // Convert the RAID level to lowercase and remove any surrounding whitespace level = strings.TrimSpace(strings.ToLower(level)) // Check if the level exists in the list of valid levels for _, validLevel := range validLevels { if level == validLevel { return true } } // Return false if the level is not found in the list of valid levels return false } // Get RAID device info from device path func (m *Manager) GetRAIDDeviceByDevicePath(devicePath string) (*RAIDDevice, error) { //Strip the /dev/ part if it was accidentally passed in devicePath = filepath.Base(devicePath) //Get all the raid devices rdevs, err := m.GetRAIDDevicesFromProcMDStat() if err != nil { return nil, err } //Check for match for _, rdev := range rdevs { if rdev.Name == devicePath { return &rdev, nil } } return nil, errors.New("target RAID device not found") } // Check if a RAID device exists, e.g. md0 func (m *Manager) RAIDDeviceExists(devicePath string) bool { _, err := m.GetRAIDDeviceByDevicePath(devicePath) return err == nil }