123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package raid
- import (
- "fmt"
- "os"
- "os/exec"
- "strconv"
- "strings"
- )
- /*
- mdadm manager
- This script handles the interaction with mdadm
- */
- type StorageDeviceMeta struct {
- Name string
- Size int64
- RO bool
- DevType string
- MountPoint string
- }
- // List all the storage device in the system, set minSize to 0 for no filter
- func (m *Manager) ListAllStorageDevices(minSize int64) ([]*StorageDeviceMeta, error) {
- cmd := exec.Command("sudo", "lsblk", "-b")
- output, err := cmd.CombinedOutput()
- if err != nil {
- return nil, fmt.Errorf("lsblk error: %v", err)
- }
- // Split the output into lines
- lines := strings.Split(string(output), "\n")
- var devices []*StorageDeviceMeta
- // Parse each line to extract device information
- for _, line := range lines[1:] { // Skip the header line
- fields := strings.Fields(line)
- if len(fields) < 7 {
- continue
- }
- size, err := strconv.ParseInt(fields[3], 10, 64)
- if err != nil {
- return nil, fmt.Errorf("error parsing device size: %v", err)
- }
- ro := fields[4] == "1"
- device := &StorageDeviceMeta{
- Name: fields[0],
- Size: size,
- RO: ro,
- DevType: fields[5],
- MountPoint: fields[6],
- }
- // Filter devices based on minimum size
- if size >= minSize {
- devices = append(devices, device)
- }
- }
- return devices, nil
- }
- // Return the uuid of the disk by its path name (e.g. /dev/sda)
- func (m *Manager) GetDiskUUIDByPath(devicePath string) (string, error) {
- cmd := exec.Command("sudo", "blkid", devicePath)
- output, err := cmd.CombinedOutput()
- if err != nil {
- return "", fmt.Errorf("blkid error: %v", err)
- }
- // Parse the output to extract the UUID
- fields := strings.Fields(string(output))
- for _, field := range fields {
- if strings.HasPrefix(field, "UUID=") {
- uuid := strings.TrimPrefix(field, "UUID=\"")
- uuid = strings.TrimSuffix(uuid, "\"")
- return uuid, nil
- }
- }
- return "", fmt.Errorf("UUID not found for device %s", devicePath)
- }
- // CreateRAIDDevice creates a RAID device using the mdadm command.
- func (m *Manager) CreateRAIDDevice(devName string, raidName string, raidLevel int, raidDeviceIds []string, spareDeviceIds []string) error {
- //Calculate the size of the raid devices
- raidDev := len(raidDeviceIds)
- spareDevice := len(spareDeviceIds)
- //Validate the number of disk is enough for the raid
- if raidLevel == 0 && raidDev < 2 {
- return fmt.Errorf("not enough disks for raid0")
- } else if raidLevel == 1 && raidDev < 2 {
- return fmt.Errorf("not enough disks for raid1")
- } else if raidLevel == 5 && raidDev < 3 {
- return fmt.Errorf("not enough disk for raid5")
- } else if raidLevel == 6 && raidDev < 4 {
- return fmt.Errorf("not enough disk for raid6")
- }
- // Concatenate RAID and spare device arrays
- allDeviceIds := append(raidDeviceIds, spareDeviceIds...)
- // Build the mdadm command
- cmd := exec.Command("bash", "-c", fmt.Sprintf("yes | sudo mdadm --create %s --name %s --level=%d --raid-devices=%d --spare-devices=%d %s",
- devName, raidName, raidLevel, raidDev, spareDevice, strings.Join(allDeviceIds, " ")))
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- err := cmd.Run()
- if err != nil {
- return fmt.Errorf("error running mdadm command: %v", err)
- }
- return nil
- }
- // Updates the mdadm configuration file with the details of RAID arrays
- // so the RAID drive will still be seen after a reboot (hopefully)
- func UpdateMDADMConfig() error {
- cmdMdadm := exec.Command("sudo", "mdadm", "--detail", "--scan", "--verbose")
- // Run the command and capture its output
- output, err := cmdMdadm.Output()
- if err != nil {
- return fmt.Errorf("error running mdadm command: %v", err)
- }
- //Load the config from system
- currentConfigBytes, err := os.ReadFile("/etc/mdadm/mdadm.conf")
- if err != nil {
- return fmt.Errorf("unable to open mdadm.conf: " + err.Error())
- }
- currentConf := string(currentConfigBytes)
- //Check if the current config already contains the setting
- newConfigLines := []string{}
- arrayConfigs := strings.TrimSpace(string(output))
- lines := strings.Split(arrayConfigs, "ARRAY")
- for _, line := range lines {
- //For each line, you should have something like this
- //ARRAY /dev/md0 metadata=1.2 name=debian:0 UUID=cbc11a2b:fbd42653:99c1340b:9c4962fb
- // devices=/dev/sdb,/dev/sdc
- line = strings.ReplaceAll(line, "\n", " ")
- fields := strings.Fields(line)
- poolUUID := strings.TrimPrefix(fields[3], "UUID=")
- //Check if this uuid already in the config file
- if strings.Contains(currentConf, poolUUID) {
- continue
- }
- //This config not exists in the settings. Add it to append lines
- settingLine := "ARRAY " + strings.Join(fields, " ")
- newConfigLines = append(newConfigLines, settingLine)
- }
- fmt.Println(newConfigLines)
- //TODO: APPEND THE LINES INTO THE CONFIG FILE
- return nil
- }
|