123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- 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
- }
|