Эх сурвалжийг харах

Finished RAID creation and mount system

aroz 1 жил өмнө
parent
commit
9e039e934a

+ 50 - 29
disk.go

@@ -19,6 +19,35 @@ import (
 	"imuslab.com/arozos/mod/utils"
 )
 
+func RAIDServiceInit() {
+	/*
+		RAID Management
+
+		Handle physical disk RAID for more NAS OS like experience
+	*/
+
+	if *allow_hardware_management {
+		rm, err := raid.NewRaidManager(raid.Options{})
+		if err == nil {
+			raidManager = rm
+
+		} else {
+			//Unable to start RAID manager. Skip it.
+			systemWideLogger.PrintAndLog("RAID", "Unable to start RAID manager", err)
+		}
+
+		/* Flush mdadm RAID */
+		if raidManager != nil {
+			if !*skip_mdadm_reload {
+				err := raidManager.FlushReload()
+				if err != nil {
+					systemWideLogger.PrintAndLog("RAID", "mdadm reload failed: "+err.Error(), err)
+				}
+			}
+		}
+	}
+}
+
 func DiskServiceInit() {
 	//Register Disk Utilities under System Setting
 	//Disk info are only viewable by administrator
@@ -110,38 +139,30 @@ func DiskServiceInit() {
 
 				authRouter.HandleFunc("/system/disk/smart/getSMART", smartListener.GetSMART)
 			}
-		}
 
-		/*
-			RAID Management
-
-			Handle physical disk RAID for more NAS OS like experience
-		*/
-		if *allow_hardware_management {
-			//Register endpoints and settings for this host
-			registerSetting(settingModule{
-				Name:         "RAID",
-				Desc:         "Providing basic mdadm features",
-				IconPath:     "SystemAO/disk/raid/img/small_icon.png",
-				Group:        "Disk",
-				StartDir:     "SystemAO/disk/raid/index.html",
-				RequireAdmin: true,
-			})
-
-			rm, err := raid.NewRaidManager(raid.Options{})
-			if err == nil {
-				raidManager = rm
+			/*
+				RAID Manager endpoints
+			*/
+			if raidManager != nil {
+				//Register endpoints and settings for this host
+				registerSetting(settingModule{
+					Name:         "RAID",
+					Desc:         "Providing basic mdadm features",
+					IconPath:     "SystemAO/disk/raid/img/small_icon.png",
+					Group:        "Disk",
+					StartDir:     "SystemAO/disk/raid/index.html",
+					RequireAdmin: true,
+				})
 
-			} else {
-				//Unable to start RAID manager. Skip it.
-				systemWideLogger.PrintAndLog("RAID", "Unable to start RAID manager", err)
-			}
+				/* RAID storage pool function */
+				adminRouter.HandleFunc("/system/disk/raid/list", raidManager.HandleListRaidDevices)
+				adminRouter.HandleFunc("/system/disk/raid/new", raidManager.HandleCreateRAIDDevice)
+				adminRouter.HandleFunc("/system/disk/raid/remove", raidManager.HandleRemoveRaideDevice)
+				adminRouter.HandleFunc("/system/disk/raid/format", raidManager.HandleFormatRaidDevice)
 
-			if raidManager != nil {
-				authRouter.HandleFunc("/system/disk/raid/list", raidManager.HandleListRaidDevices)
-				authRouter.HandleFunc("/system/disk/raid/remove", raidManager.HandleRemoveRaideDevice)
-				authRouter.HandleFunc("/system/disk/raid/assemble", raidManager.HandleRaidDevicesAssemble)
-				authRouter.HandleFunc("/system/disk/raid/new", raidManager.HandleCreateRAIDDevice)
+				/* Advance functions*/
+				adminRouter.HandleFunc("/system/disk/raid/assemble", raidManager.HandleRaidDevicesAssemble)
+				adminRouter.HandleFunc("/system/disk/raid/reload", raidManager.HandleMdadmFlushReload)
 			}
 		}
 

+ 1 - 0
main.flags.go

@@ -74,6 +74,7 @@ var allow_hardware_management = flag.Bool("enable_hwman", true, "Enable hardware
 var allow_power_management = flag.Bool("enable_pwman", true, "Enable power management of the host system")
 var wpa_supplicant_path = flag.String("wpa_supplicant_config", "/etc/wpa_supplicant/wpa_supplicant.conf", "Path for the wpa_supplicant config")
 var wan_interface_name = flag.String("wlan_interface_name", "wlan0", "The default wireless interface for connecting to an AP")
+var skip_mdadm_reload = flag.Bool("skip_mdadm_reload", false, "Skip mdadm reload config during startup, might result in werid RAID device ID in some Linux distro")
 
 // Flags related to files and uploads
 var max_upload = flag.Int("max_upload_size", 8192, "Maxmium upload size in MB. Must not exceed the available ram on your system")

+ 3 - 6
mod/apt/apt.go

@@ -33,8 +33,8 @@ func (a *AptPackageManager) InstallIfNotExists(pkgname string, mustComply bool)
 	pkgname = strings.ReplaceAll(pkgname, "&", "")
 	pkgname = strings.ReplaceAll(pkgname, "|", "")
 
-	if a.AllowAutoInstall == false {
-		return errors.New("Package auto install is disabled")
+	if !a.AllowAutoInstall {
+		return errors.New("package auto install is disabled")
 	}
 
 	installed, err := PackageExists(pkgname)
@@ -109,10 +109,8 @@ func PackageExists(pkgname string) (bool, error) {
 		}
 
 	} else {
-		return false, errors.New("Unsupported Platform")
+		return false, errors.New("unsupported Platform")
 	}
-
-	return false, errors.New("Unknown error occured when checking package installed")
 }
 
 func HandlePackageListRequest(w http.ResponseWriter, r *http.Request) {
@@ -153,5 +151,4 @@ func HandlePackageListRequest(w http.ResponseWriter, r *http.Request) {
 	jsonString, _ := json.Marshal(results)
 	w.Header().Set("Content-Type", "application/json")
 	w.Write(jsonString)
-	return
 }

+ 123 - 0
mod/disk/diskfs/diskfs.go

@@ -0,0 +1,123 @@
+package diskfs
+
+import (
+	"errors"
+	"fmt"
+	"os/exec"
+	"strconv"
+	"strings"
+
+	"imuslab.com/arozos/mod/utils"
+)
+
+/*
+	diskfs.go
+
+	This module handle file system creation and formatting
+*/
+
+// Storage Device meta was generated by lsblk
+type StorageDeviceMeta struct {
+	Name       string
+	Size       int64
+	RO         bool
+	DevType    string
+	MountPoint string
+}
+
+// Check if the file format driver is installed on this host
+// if a format is supported, mkfs.(format) should be symlinked under /sbin
+func FormatPackageInstalled(fsType string) bool {
+	return utils.FileExists("/sbin/mkfs." + fsType)
+}
+
+// Create file system, support ntfs, ext4 and fat32 only
+func FormatStorageDevice(fsType string, devicePath string) error {
+	// Check if the filesystem type is supported
+	switch fsType {
+	case "ext4":
+		// Format the device with the specified filesystem type
+		cmd := exec.Command("sudo", "mkfs."+fsType, devicePath)
+		output, err := cmd.CombinedOutput()
+		if err != nil {
+			return errors.New("unable to format device: " + string(output))
+		}
+		return nil
+
+	case "vfat", "fat", "fat32":
+		//Check if mkfs.fat exists
+		if !FormatPackageInstalled("vfat") {
+			return errors.New("unable to format device as fat (vfat). dosfstools not installed?")
+		}
+
+		// Format the device with the specified filesystem type
+		cmd := exec.Command("sudo", "mkfs.vfat", devicePath)
+		output, err := cmd.CombinedOutput()
+		if err != nil {
+			return errors.New("unable to format device: " + string(output))
+		}
+		return nil
+
+	case "ntfs":
+		//Check if ntfs-3g exists
+		if !FormatPackageInstalled("ntfs") {
+			return errors.New("unable to format device as ntfs: ntfs-3g not installed?")
+		}
+
+		//Format the drive
+		cmd := exec.Command("sudo", "mkfs.ntfs", devicePath)
+		output, err := cmd.CombinedOutput()
+		if err != nil {
+			return errors.New("unable to format device: " + string(output))
+		}
+		return nil
+
+	default:
+		return fmt.Errorf("unsupported filesystem type: %s", fsType)
+	}
+}
+
+// List all the storage device in the system, set minSize to 0 for no filter
+func 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
+}

+ 47 - 0
mod/disk/raid/handler.go

@@ -8,6 +8,7 @@ import (
 	"strconv"
 	"strings"
 
+	"imuslab.com/arozos/mod/disk/diskfs"
 	"imuslab.com/arozos/mod/utils"
 )
 
@@ -17,6 +18,51 @@ import (
 	This module handle api call to the raid module
 */
 
+// Handle force flush reloading mdadm to solve the md0 become md127 problem
+func (m *Manager) HandleMdadmFlushReload(w http.ResponseWriter, r *http.Request) {
+	err := m.FlushReload()
+	if err != nil {
+		utils.SendErrorResponse(w, "reload failed: "+err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+// Handle formating a device
+func (m *Manager) HandleFormatRaidDevice(w http.ResponseWriter, r *http.Request) {
+	devName, err := utils.GetPara(r, "devName")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid device name given")
+		return
+	}
+
+	format, err := utils.GetPara(r, "format")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid device name given")
+		return
+	}
+
+	if !strings.HasPrefix(devName, "/dev/") {
+		devName = "/dev/" + devName
+	}
+
+	//Check if the target device exists
+	if !m.RAIDDeviceExists(devName) {
+		utils.SendErrorResponse(w, "target not exists or not a valid RAID device")
+		return
+	}
+
+	//Format the drive
+	err = diskfs.FormatStorageDevice(format, devName)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
 // List all the raid device in this system
 func (m *Manager) HandleListRaidDevices(w http.ResponseWriter, r *http.Request) {
 	rdev, err := m.GetRAIDDevicesFromProcMDStat()
@@ -31,6 +77,7 @@ func (m *Manager) HandleListRaidDevices(w http.ResponseWriter, r *http.Request)
 
 // Create a RAID storage pool
 func (m *Manager) HandleCreateRAIDDevice(w http.ResponseWriter, r *http.Request) {
+	//TODO: Change GetPara to Post
 	devName, err := utils.GetPara(r, "devName")
 	if err != nil {
 		utils.SendErrorResponse(w, "invalid device name given")

+ 0 - 54
mod/disk/raid/mdadm.go

@@ -19,15 +19,6 @@ import (
 	This script handles the interaction with mdadm
 */
 
-// Storage Device meta was generated by lsblk
-type StorageDeviceMeta struct {
-	Name       string
-	Size       int64
-	RO         bool
-	DevType    string
-	MountPoint string
-}
-
 // RAIDDevice represents information about a RAID device.
 type RAIDMember struct {
 	Name string
@@ -41,51 +32,6 @@ type RAIDDevice struct {
 	Members []*RAIDMember
 }
 
-// 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)

+ 42 - 0
mod/disk/raid/mdadmConf.go

@@ -6,6 +6,7 @@ import (
 	"os"
 	"os/exec"
 	"strings"
+	"time"
 
 	"imuslab.com/arozos/mod/utils"
 )
@@ -19,6 +20,47 @@ import (
 
 */
 
+// Force mdadm to stop all RAID and load fresh from config file
+// on some Linux distro this is required as mdadm start too early
+func (m *Manager) FlushReload() error {
+	//Get a list of currently running RAID devices
+	raidDevices, err := m.GetRAIDDevicesFromProcMDStat()
+	if err != nil {
+		return err
+	}
+
+	//Stop all of the running RAID devices
+	for _, rd := range raidDevices {
+
+		//Check if it is mounted. If yes, skip this
+		devMounted, err := DeviceIsMounted("/dev/" + rd.Name)
+		if devMounted || err != nil {
+			log.Println("[RAID] " + "/dev/" + rd.Name + " is in use. Skipping.")
+			continue
+		}
+		log.Println("[RAID] Stopping " + rd.Name)
+
+		cmdMdadm := exec.Command("sudo", "mdadm", "--stop", "/dev/"+rd.Name)
+
+		// Run the command and capture its output
+		_, err = cmdMdadm.Output()
+		if err != nil {
+			log.Println("[RAID] Unable to stop " + rd.Name + ". Skipping")
+			continue
+		}
+	}
+
+	time.Sleep(300 * time.Millisecond)
+
+	//Assemble mdadm array again
+	err = m.RestartRAIDService()
+	if err != nil {
+		return 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 (m *Manager) UpdateMDADMConfig() error {

+ 2 - 1
startup.go

@@ -61,7 +61,8 @@ func RunStartup() {
 	permissionNewHandler() //See permission.go
 
 	//4. Mount and create the storage system base
-	StorageInit() //See storage.go
+	RAIDServiceInit() //See disk.go, this must run before Storage Init
+	StorageInit()     //See storage.go
 
 	//5. Startup user and permission sytem
 	UserSystemInit()        //See user.go

+ 4 - 1
web/Music/embedded.html

@@ -596,7 +596,10 @@
 			$("#playbackProgress").css("background-color", colorText);
 			$("#playpauseBtn").css("background-color", colorText);
 			$("#volControl").css("background-color", colorText);
-			$(".modeEnabled")[0].style.setProperty( "background-color", colorText, 'important' );
+			if ($(".modeEnabled").length > 0){
+				$(".modeEnabled")[0].style.setProperty( "background-color", colorText, 'important' );
+			}
+			
 			//$(".sameDirFileList").css("background-color", `rgba(${newRGBColor.r}, ${newRGBColor.g}, ${newRGBColor.b}, 0.1)`);
 		}