aroz 1 жил өмнө
parent
commit
f38a43b8ac

+ 1 - 0
disk.go

@@ -160,6 +160,7 @@ func DiskServiceInit() {
 				adminRouter.HandleFunc("/system/disk/raid/remove", raidManager.HandleRemoveRaideDevice)
 				adminRouter.HandleFunc("/system/disk/raid/format", raidManager.HandleFormatRaidDevice)
 				adminRouter.HandleFunc("/system/disk/raid/detail", raidManager.HandleLoadArrayDetail)
+				adminRouter.HandleFunc("/system/disk/raid/devinfo", raidManager.HandlListChildrenDeviceInfo)
 
 				/* Device Management functions */
 				adminRouter.HandleFunc("/system/disk/devices/list", raidManager.HandleListUsableDevices)

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

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"regexp"
 	"strings"
 
 	"imuslab.com/arozos/mod/utils"
@@ -113,6 +114,69 @@ func ListAllStorageDevices() (*StorageDevicesMeta, error) {
 	return &devices, err
 }
 
+// Get block device (e.g. /dev/sdX) info
+func GetBlockDeviceMeta(devicePath string) (*BlockDeviceMeta, error) {
+	//Trim the /dev/ part of the device path
+	deviceName := strings.TrimPrefix(devicePath, "/dev/")
+
+	if len(deviceName) == 0 {
+		return nil, errors.New("invalid device path given")
+	}
+
+	re := regexp.MustCompile(`\d+`)
+	if re.MatchString(deviceName) {
+		//This is a partition
+		return nil, errors.New("given device path is a partition not a block device")
+	}
+
+	storageMeta, err := ListAllStorageDevices()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, blockdevice := range storageMeta.Blockdevices {
+		if blockdevice.Name == deviceName {
+			return &blockdevice, nil
+		}
+	}
+
+	return nil, errors.New("target block device not found")
+}
+
+// Get partition information (e.g. /dev/sdX1)
+func GetPartitionMeta(devicePath string) (*PartitionMeta, error) {
+	//Trim the /dev/ part of the device path
+	deviceName := strings.TrimPrefix(devicePath, "/dev/")
+
+	if len(deviceName) == 0 {
+		return nil, errors.New("invalid device path given")
+	}
+
+	re := regexp.MustCompile(`\d+`)
+	if !re.MatchString(deviceName) {
+		//This is a partition
+		return nil, errors.New("given device path is a block device not a partition")
+	}
+
+	storageMeta, err := ListAllStorageDevices()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, blockdevice := range storageMeta.Blockdevices {
+		if strings.Contains(deviceName, blockdevice.Name) {
+			//Matching block device. Check for if there are a matching child
+			for _, childPartition := range blockdevice.Children {
+				if childPartition.Name == deviceName {
+					return &childPartition, nil
+				}
+			}
+		}
+	}
+
+	return nil, errors.New("target partition not found")
+}
+
 // Check if a device is mounted given the path name, like /dev/sdc
 func DeviceIsMounted(devicePath string) (bool, error) {
 	// Open the mountinfo file

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

@@ -28,6 +28,46 @@ func (m *Manager) HandleMdadmFlushReload(w http.ResponseWriter, r *http.Request)
 	utils.SendOK(w)
 }
 
+// Handle force flush reloading mdadm to solve the md0 become md127 problem
+func (m *Manager) HandlListChildrenDeviceInfo(w http.ResponseWriter, r *http.Request) {
+	devName, err := utils.GetPara(r, "devName")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid device name given")
+		return
+	}
+
+	if !strings.HasPrefix(devName, "/dev/") {
+		devName = "/dev/" + devName
+	}
+
+	//Get the children devices for this RAID
+	raidDevice, err := m.GetRAIDDeviceByDevicePath(devName)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//Merge the child devices info into one array
+	results := map[string]*diskfs.BlockDeviceMeta{}
+	for _, blockdevice := range raidDevice.Members {
+		bdm, err := diskfs.GetBlockDeviceMeta("/dev/" + blockdevice.Name)
+		if err != nil {
+			log.Println("[RAID] Unable to load block device info: " + err.Error())
+			results[blockdevice.Name] = &diskfs.BlockDeviceMeta{
+				Name: blockdevice.Name,
+				Size: -1,
+			}
+
+			continue
+		}
+
+		results[blockdevice.Name] = bdm
+	}
+
+	js, _ := json.Marshal(results)
+	utils.SendJSONResponse(w, string(js))
+}
+
 // Handle list all the disks that is usable
 func (m *Manager) HandleListUsableDevices(w http.ResponseWriter, r *http.Request) {
 	storageDevices, err := diskfs.ListAllStorageDevices()

+ 11 - 0
mod/disk/raid/raiddetails.go

@@ -36,6 +36,7 @@ type RAIDInfo struct {
 type DeviceInfo struct {
 	State      []string
 	DevicePath string
+	RaidDevice int //Sequence of the raid device?
 }
 
 // GetRAIDInfo retrieves information about a RAID array using the mdadm command.
@@ -111,6 +112,15 @@ func parseRAIDInfo(output string) *RAIDInfo {
 				if len(fields) >= 5 && fields[0] != "Number" {
 					deviceInfo := DeviceInfo{}
 
+					if len(fields) > 3 {
+						rdNo, err := strconv.Atoi(fields[3])
+						if err != nil {
+							rdNo = -1
+						}
+						deviceInfo.RaidDevice = rdNo
+
+					}
+
 					if len(fields) > 5 {
 						//Only active disks have fields > 5, e.g.
 						// 0       8       16        0      active sync   /dev/sdb
@@ -119,6 +129,7 @@ func parseRAIDInfo(output string) *RAIDInfo {
 					} else {
 						//Failed disk, e.g.
 						//  -       0        0        1      removed
+
 						deviceInfo.State = fields[4:]
 						//TODO: Add custom tags
 					}

+ 35 - 0
web/SystemAO/disk/raid/img/drive-notfound.svg

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
+	 height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<g id="圖層_1">
+	<rect x="12.458" y="81.333" fill="#727171" width="102.833" height="29.167"/>
+	<polygon fill="#DCDDDD" points="96.374,40.167 30.208,40.167 12.542,81.333 115.374,81.333 	"/>
+	<path fill="#3E3A39" d="M113.125,105.811c0,1.728-1.358,3.127-3.034,3.127H17.535c-1.676,0-3.035-1.399-3.035-3.127V86.002
+		c0-1.728,1.359-3.127,3.035-3.127h92.556c1.676,0,3.034,1.399,3.034,3.127V105.811z"/>
+	<circle fill="#00A0E9" cx="100.375" cy="95.916" r="3.708"/>
+</g>
+<g id="圖層_2">
+</g>
+<g id="圖層_3">
+	<circle fill="#3E3A39" cx="94.833" cy="36" r="25.5"/>
+	<g>
+		<path fill="#F7F8F8" d="M91.648,40.194v-1.445c0-0.664,0.068-1.266,0.205-1.807c0.137-0.54,0.346-1.055,0.625-1.543
+			c0.28-0.488,0.642-0.963,1.084-1.426c0.443-0.462,0.977-0.94,1.602-1.436c0.547-0.43,1.01-0.813,1.387-1.152
+			c0.378-0.338,0.684-0.674,0.918-1.006s0.404-0.68,0.508-1.045c0.104-0.364,0.156-0.788,0.156-1.27c0-0.742-0.25-1.354-0.752-1.836
+			c-0.501-0.481-1.266-0.723-2.295-0.723c-0.898,0-1.865,0.189-2.9,0.566c-1.035,0.378-2.105,0.84-3.213,1.387l-1.992-4.316
+			c0.561-0.325,1.169-0.635,1.826-0.928c0.658-0.293,1.342-0.553,2.051-0.781c0.71-0.228,1.43-0.407,2.158-0.537
+			c0.729-0.13,1.445-0.195,2.148-0.195c1.328,0,2.526,0.16,3.594,0.479c1.068,0.319,1.973,0.785,2.715,1.396
+			c0.742,0.612,1.313,1.354,1.709,2.227c0.398,0.873,0.596,1.869,0.596,2.988c0,0.82-0.09,1.553-0.273,2.197
+			c-0.182,0.645-0.452,1.244-0.811,1.797c-0.357,0.554-0.807,1.087-1.348,1.602c-0.54,0.515-1.168,1.058-1.885,1.631
+			c-0.547,0.43-0.992,0.804-1.338,1.123c-0.345,0.319-0.615,0.622-0.811,0.908c-0.195,0.287-0.328,0.583-0.4,0.889
+			c-0.071,0.306-0.107,0.667-0.107,1.084v1.172H91.648z M91.004,46.874c0-0.612,0.088-1.129,0.264-1.553
+			c0.176-0.423,0.42-0.765,0.732-1.025c0.313-0.26,0.681-0.449,1.104-0.566c0.424-0.117,0.876-0.176,1.357-0.176
+			c0.456,0,0.889,0.059,1.299,0.176s0.771,0.306,1.084,0.566c0.313,0.261,0.561,0.603,0.742,1.025
+			c0.183,0.423,0.273,0.941,0.273,1.553c0,0.586-0.091,1.087-0.273,1.504c-0.182,0.417-0.43,0.762-0.742,1.035
+			s-0.674,0.472-1.084,0.596c-0.41,0.124-0.843,0.186-1.299,0.186c-0.481,0-0.934-0.062-1.357-0.186
+			c-0.423-0.124-0.791-0.322-1.104-0.596s-0.557-0.618-0.732-1.035C91.092,47.961,91.004,47.459,91.004,46.874z"/>
+	</g>
+</g>
+</svg>

+ 101 - 31
web/SystemAO/disk/raid/index.html

@@ -77,6 +77,32 @@
                 font-weight: 300;
             }
 
+            /* RAID device members */
+            .raiddevice{
+                border-radius: 0.4em !important;
+                margin-bottom: 0 !important;
+                margin-top: 0 !important;
+                cursor: pointer;
+                position: relative;
+            }
+            .raiddevice:hover{
+                background-color: #f3f3f3 !important;
+            }
+            .raiddevice .capinfo{
+                position: absolute;
+                color:rgb(136, 136, 136);
+                right: 1em;
+                top: 1em;
+                text-align: right;
+            }
+            .raiddevice .capinfo .readonlytag{
+                background-color: #cc3d3a; 
+                color:white; 
+                padding: 2px; 
+                padding-left: 5px;
+                padding-right: 5px;
+            }
+
             /* Danger zone css */
             .dangerzone{
                 border: 1px solid #dedede;
@@ -125,8 +151,8 @@
                             <h3 class="ui header">
                                 <i class="ui green check circle icon"></i>
                                 <div class="content">
-                                    <span id="RAIDOverviewDiskpath">/dev/md0</span>
-                                    <div class="sub header" id="RAIDOverviewDetails">10c0a9d9:763e326a:7d825b41:e1dc0536 | clean</div>
+                                    <span id="RAIDOverviewDiskpath">Loading device details</span>
+                                    <div class="sub header" id="RAIDOverviewDetails"></div>
                                 </div>
                             </h3>
                             <div class="ui divider"></div>
@@ -174,7 +200,13 @@
                         <br>
                         <div id="raidDiskList">
                             <div class="ui basic segment">
-
+                                <h4 class="ui header">
+                                    <img src="../disk/raid/img/drive-working.svg">
+                                    <div class="content">
+                                      /dev/sdb
+                                      <div class="sub header">Check out our plug-in marketplace</div>
+                                    </div>
+                                  </h4>
                             </div>
                         </div>
                         <div class="ui divider"></div>
@@ -260,37 +292,75 @@
                     return string.charAt(0).toUpperCase() + string.slice(1);
                 }
 
-                //Get the information of the device
-                $.get("../../system/disk/raid/detail?devName=" + deviceName, function(data){
-                    if (data.error != undefined){
-                        $("#raidDiskList").html(`<h3 class="ui header">
-                            <i class="ui red circle times icon"></i> 
-                            <div class="content" style="font-weight: 300;">
-                                Unable to load RAID volume information 
-                                <div class="sub header">${capitalize(data.error)}</div>
-                            </div>
-                            
-                        </h3>`);
-                        return;
-                    }
+                function bytesToSize(bytes) {
+                    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
+                    if (bytes == 0) return 'n/a';
+                    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
+                    if (i == 0) return bytes + ' ' + sizes[i];
+                    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
+                };
 
-                   
+                let deiviceSizeMap = {};
+                //Load the device size map
+                $.get("../../system/disk/raid/devinfo?devName=" + deviceName, function(sizemap){
+                    deiviceSizeMap = sizemap;
+                    //Get the information of the device
+                    $.get("../../system/disk/raid/detail?devName=" + deviceName, function(data){
+                        if (data.error != undefined){
+                            $("#raidDiskList").html(`<h3 class="ui header">
+                                <i class="ui red circle times icon"></i> 
+                                <div class="content" style="font-weight: 300;">
+                                    Unable to load RAID volume information 
+                                    <div class="sub header">${capitalize(data.error)}</div>
+                                </div>
+                                
+                            </h3>`);
+                            return;
+                        }
 
-                    //Update the active disks info
-                    $("#RAIDOverviewDiskpath").text(data.DevicePath + `(${data.RaidLevel})`);
-                    $("#RAIDOverviewDetails").text(data.UUID + " | State: " + capitalize(data.State));
-                    $("#RAIDActiveDevices").text(data.ActiveDevices);
-                    $("#RAIDWorkingDevices").text(data.WorkingDevices);
-                    $("#RAIDFailedDevices").text(data.FailedDevices);
-                    $("#RAIDSpareDevices").text(data.SpareDevices);
+                    
 
-                     //Render the disk list
-                     //$("#raidDiskList").html("");
-                    for (var i = 0; i < data.DeviceInfo.length; i++){
-                        let thisDeviceInfo = data.DeviceInfo[i];
-                        $("#raidDiskList").append(``);
-                    }
+                        //Update the active disks info
+                        $("#RAIDOverviewDiskpath").html(data.DevicePath + ` <span class="mdevice">(${data.RaidLevel.toUpperCase()})</span>`);
+                        $("#RAIDOverviewDetails").text(data.UUID + " | State: " + capitalize(data.State));
+                        $("#RAIDActiveDevices").text(data.ActiveDevices);
+                        $("#RAIDWorkingDevices").text(data.WorkingDevices);
+                        $("#RAIDFailedDevices").text(data.FailedDevices);
+                        $("#RAIDSpareDevices").text(data.SpareDevices);
+
+                        //Render the disk list
+                        $("#raidDiskList").html("");
+                        for (var i = 0; i < data.DeviceInfo.length; i++){
+                            let thisDeviceInfo = data.DeviceInfo[i];
+                            let raidDeviceNo = thisDeviceInfo.RaidDevice;
+                            let drivePath = thisDeviceInfo.DevicePath
+                            let driveName = drivePath.split("/").pop();
+                            let driveIcon = "drive-working.svg";
+                            if (thisDeviceInfo.DevicePath = ""){
+                                driveIcon = "drive-notfound.svg"
+                                drivePath = "(Drive unplugged or damaged)"
+                            }
+
+
+                            $("#raidDiskList").append(`<div class="ui basic segment raiddevice">
+                                <h4 class="ui header">
+                                    <img src="../disk/raid/img/${driveIcon}">
+                                    <div class="content">
+                                        ${raidDeviceNo}: ${drivePath}
+                                        <div class="sub header">${thisDeviceInfo.State.join(" | ")}</div>
+                                    </div>
+                                </h4>
+                                <div class="capinfo">
+                                    ${bytesToSize(deiviceSizeMap[driveName].size)}<br>
+                                    <span style="font-size: 0.8em;">${deiviceSizeMap[driveName].ro?"<span class='readonlytag'>Read Only</span>":"Read Write"}</span>
+                                </div>
+                                <div class="raidDeviceOptions">
+
+                                </div>
+                            </div>`);
+                        }
 
+                    });
                 });
             }
 
@@ -312,7 +382,7 @@
                                 <img src="../../img/system/raid.svg">
                                 <div class="healthIcon"><i class="${getStateIconFromStateText(thisDiskInfo.State)}"></i></div>
                                 <div class="content" style="margin-left: 0.6em;">
-                                    <span>${thisDiskInfo.DevicePath}</span> <span class="mdevice">(${thisDiskInfo.RaidLevel})</span>
+                                    <span>${thisDiskInfo.DevicePath}</span> <span class="mdevice">(${thisDiskInfo.RaidLevel.toUpperCase()})</span>
                                     <div class="sub header mdevuuid" style="margin-top: 0.4em;">
                                         ${thisDiskInfo.UUID}
                                     </div>