瀏覽代碼

Finalized RAID health check function

aroz 1 年之前
父節點
當前提交
8a8c417bb6
共有 6 個文件被更改,包括 201 次插入8 次删除
  1. 1 0
      disk.go
  2. 57 5
      mod/disk/raid/handler.go
  3. 36 0
      mod/disk/raid/raidutils.go
  4. 10 0
      mod/disk/raid/status.go
  5. 96 2
      web/SystemAO/desktop/utils/diskoverview.html
  6. 1 1
      web/desktop.system

+ 1 - 0
disk.go

@@ -157,6 +157,7 @@ func DiskServiceInit() {
 				})
 
 				/* RAID storage pool function */
+				adminRouter.HandleFunc("/system/disk/raid/overview", raidManager.HandleRenderOverview)
 				adminRouter.HandleFunc("/system/disk/raid/list", raidManager.HandleListRaidDevices)
 				adminRouter.HandleFunc("/system/disk/raid/new", raidManager.HandleCreateRAIDDevice)
 				adminRouter.HandleFunc("/system/disk/raid/remove", func(w http.ResponseWriter, r *http.Request) {

+ 57 - 5
mod/disk/raid/handler.go

@@ -19,11 +19,6 @@ import (
 	This module handle api call to the raid module
 */
 
-// Handle stopping a RAID array for maintaince
-func (m *Manager) HandleStopRAIDArray(w http.ResponseWriter, r *http.Request) {
-
-}
-
 // Handle remove a member disk (sdX) from RAID volume (mdX)
 func (m *Manager) HandleRemoveDiskFromRAIDVol(w http.ResponseWriter, r *http.Request) {
 	//mdadm --remove /dev/md0 /dev/sdb1
@@ -627,3 +622,60 @@ func (m *Manager) HandleGrowRAIDArray(w http.ResponseWriter, r *http.Request) {
 
 	utils.SendOK(w)
 }
+
+// HandleRenderOverview List the info and health of all loaded RAID array
+func (m *Manager) HandleRenderOverview(w http.ResponseWriter, r *http.Request) {
+	//Get all raid device from procmd
+	rdevs, err := m.GetRAIDDevicesFromProcMDStat()
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	type RaidHealthOverview struct {
+		Name      string
+		Status    string
+		Level     string
+		UsedSize  int64
+		TotalSize int64
+		IsHealthy bool
+	}
+
+	results := []*RaidHealthOverview{}
+
+	//Get RAID Status for each devices
+	for _, raidDev := range rdevs {
+		//Fill in the basic information
+		thisRaidOverview := RaidHealthOverview{
+			Name:      raidDev.Name,
+			Status:    raidDev.Status,
+			Level:     raidDev.Level,
+			UsedSize:  -1,
+			TotalSize: -1,
+			IsHealthy: false,
+		}
+
+		//Get health status of RAID
+		raidPath := filepath.Join("/dev/", strings.TrimPrefix(raidDev.Name, "/dev/"))
+		raidStatus, err := GetRAIDStatus(raidPath)
+		if err == nil {
+			thisRaidOverview.IsHealthy = raidStatus.isHealthy()
+		}
+
+		// Get RAID vol size and info
+		raidPartitionSize, err := GetRAIDPartitionSize(raidPath)
+		if err == nil {
+			thisRaidOverview.TotalSize = raidPartitionSize
+		}
+
+		raidUsedSize, err := GetRAIDUsedSize(raidPath)
+		if err == nil {
+			thisRaidOverview.UsedSize = raidUsedSize
+		}
+
+		results = append(results, &thisRaidOverview)
+	}
+
+	js, _ := json.Marshal(results)
+	utils.SendJSONResponse(w, string(js))
+}

+ 36 - 0
mod/disk/raid/raidutils.go

@@ -253,3 +253,39 @@ func GetRAIDPartitionSize(devicePath string) (int64, error) {
 
 	return size, nil
 }
+
+// GetRAIDUsedSize returns the used size of the RAID array in bytes as an int64
+func GetRAIDUsedSize(devicePath string) (int64, error) {
+	// Ensure devicePath is formatted correctly
+	if !strings.HasPrefix(devicePath, "/dev/") {
+		devicePath = "/dev/" + devicePath
+	}
+
+	// Execute the df command with the device path
+	cmd := exec.Command("df", "--block-size=1", devicePath)
+	var out bytes.Buffer
+	cmd.Stdout = &out
+	if err := cmd.Run(); err != nil {
+		return 0, fmt.Errorf("failed to execute df command: %v", err)
+	}
+
+	// Parse the output to find the used size
+	lines := strings.Split(out.String(), "\n")
+	if len(lines) < 2 {
+		return 0, fmt.Errorf("unexpected df output: %s", out.String())
+	}
+
+	// The second line should contain the relevant information
+	fields := strings.Fields(lines[1])
+	if len(fields) < 3 {
+		return 0, fmt.Errorf("unexpected df output: %s", lines[1])
+	}
+
+	// The third field should be the used size in bytes
+	usedSize, err := strconv.ParseInt(fields[2], 10, 64)
+	if err != nil {
+		return 0, fmt.Errorf("failed to parse used size: %v", err)
+	}
+
+	return usedSize, nil
+}

+ 10 - 0
mod/disk/raid/status.go

@@ -63,3 +63,13 @@ func (status RAIDStatus) toString() string {
 		return "Invalid Status"
 	}
 }
+
+// Report if the RAID array is healthy
+func (status RAIDStatus) isHealthy() bool {
+	switch status {
+	case RAIDStatusNormal:
+		return true
+	default:
+		return false
+	}
+}

+ 96 - 2
web/SystemAO/desktop/utils/diskoverview.html

@@ -23,7 +23,7 @@
             margin-top: 0.6em;
         }
 
-        #diskrender .progress .bar{
+        #diskrender .progress .bar:not(.raiddev){
             background-color: rgb(82, 201, 255);
         }
 
@@ -31,17 +31,41 @@
             background-color: #b51d1d !important;
         }
 
-        #diskrender .progress .bar.raiddev{
+        #diskrender .progress .bar.raiddev.failing{
             background-color: #f5ef42 !important;
         }
 
+        #diskrender .progress .bar.raiddev.healthy{
+            background-color: #5cd858 !important;
+        }
+
         #diskrender .progress .bar .progress{
             background-color: transparent !important;
         }
+
+        #diskrender .raidVolStateIcon{
+            position: absolute;
+            left: 2.0em;
+            top: 2.5em;
+            background: white;
+            border-radius: 50%;
+            width: 1.2em;
+        }
+        
+        #diskrender .inactive.driveinfo{
+            opacity: 0.5;
+        }
+
+        #refreshDiskRenderBtn{
+            float: right;
+        }
     </style>
     <div id="diskrender">
         <div class="ui list" id="diskspaceList">
             
+        </div>
+        <div class="ui list" id="raidVolList">
+            
         </div>
     </div>
 
@@ -199,10 +223,80 @@
                     $("#diskrender .progress .bar").css({
                         "background-color": desktopThemeColor
                     });
+
+                    updateRAIDVolumeOverview();
                 }, error: function(){
                     $("#diskspaceList").html(`<div style="text-align: center; margin-top: 1em; height: 2em;"><i class="ui red ban icon"></i><i class="ui grey hdd icon"></i></div>`);
                 }
             });
         }
+
+        function updateRAIDVolumeOverview(){
+            $("#raidVolList").html("");
+            $.ajax({
+                url: "/system/disk/raid/overview",
+                method: "GET",
+                success: function(data){
+                    if (data.error != undefined){
+                        //Hide the section
+                        $("#raidVolList").hide();
+                    }else{
+                        //Render the data
+                        $("#raidVolList").show();
+                        let containUnhealthy = false;
+                        data.forEach(raidinfo => {
+                            let usedPercentage = (raidinfo.UsedSize / raidinfo.TotalSize) * 100;
+                            let colorClass = "raiddev healthy";
+                            if (!raidinfo.IsHealthy){
+                                colorClass = "raiddev failing";
+                                containUnhealthy = true;
+                            }
+
+                            let activeClass = ""
+                            if (!raidinfo.Status.includes("active")){
+                                activeClass = "inactive";
+                            }
+
+                            $("#raidVolList").append(`<div class="item ${activeClass} driveinfo">
+                                <img class="ui avatar image" src="img/system/cluster.svg">
+                                <div class="content">
+                                    <div class="header">${raidinfo.Name} (${raidinfo.Level.toUpperCase()} | ${raidinfo.Status})
+                                        <span style="float: right;font-size: 0.85em;">${usedPercentage.toFixed(1)}% | ${ao_module_utils.formatBytes(raidinfo.TotalSize, 1)}</span>    
+                                    </div>
+                                    <div class="description">
+                                        <div class="ui active small fluid progress diskspace">
+                                            <div class="bar ${colorClass}" style="width: ${usedPercentage}%">
+                                            <div class="progress"></div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <span class="raidVolStateIcon">${raidinfo.IsHealthy?'<i class="ui green check circle icon"></i>':'<i class="ui red times circle icon"></i>'}</span>
+                                </div>`);
+                        });
+                        
+                        if (data.length == 0){
+                            //No raid devices
+                        }
+
+                        if (containUnhealthy){
+                            //Set require attension
+                            setTimeout(function(){
+                                updateSystemOverviewStatusText(1);
+                            }, 1000);
+                        }
+                    }
+                },
+                error: function(){
+                    //Unknown error, hide raid vol list
+                    $("#raidVolList").hide();
+                }
+            });
+        }
+
+        //Update the overview every 15 minutes
+        setInterval(function(){
+            updateDiskSpaceOverview();
+        }, 900 * 1000);
     </script>
 </div>

+ 1 - 1
web/desktop.system

@@ -706,7 +706,7 @@
             pointer-events: auto;
         }
 
-        body.whiteTheme .item:hover{
+        body.whiteTheme .item:not(.clickable):hover{
             background-color: #ececec !important;
         }