Prechádzať zdrojové kódy

Added working add disk function

aroz 1 rok pred
rodič
commit
597fb8cad0

+ 32 - 0
auth.go

@@ -147,3 +147,35 @@ func AuthSettingsInit() {
 		utils.SendJSONResponse(w, string(js))
 	})
 }
+
+// Validate secure request that use authreq.html
+// Require POST: password and admin permission
+// return true if authentication passed
+func AuthValidateSecureRequest(w http.ResponseWriter, r *http.Request) bool {
+	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		w.WriteHeader(http.StatusUnauthorized)
+		w.Write([]byte("401 Unauthorized"))
+		return false
+	}
+
+	if !userinfo.IsAdmin() {
+		utils.SendErrorResponse(w, "Permission Denied")
+		return false
+	}
+
+	//Double check password for this user
+	password, err := utils.PostPara(r, "password")
+	if err != nil {
+		utils.SendErrorResponse(w, "Password Incorrect")
+		return false
+	}
+
+	passwordCorrect, rejectionReason := authAgent.ValidateUsernameAndPasswordWithReason(userinfo.Username, password)
+	if !passwordCorrect {
+		utils.SendErrorResponse(w, rejectionReason)
+		return false
+	}
+
+	return true
+}

+ 6 - 27
disk.go

@@ -161,37 +161,16 @@ func DiskServiceInit() {
 				adminRouter.HandleFunc("/system/disk/raid/format", raidManager.HandleFormatRaidDevice)
 				adminRouter.HandleFunc("/system/disk/raid/detail", raidManager.HandleLoadArrayDetail)
 				adminRouter.HandleFunc("/system/disk/raid/devinfo", raidManager.HandlListChildrenDeviceInfo)
-				adminRouter.HandleFunc("/system/disk/raid/removeMemeber", func(w http.ResponseWriter, r *http.Request) {
-					/*
-						Remove a member disk from RAID array
-						Require double check for credentials
-					*/
-					userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
-					if err != nil {
-						w.WriteHeader(http.StatusUnauthorized)
-						w.Write([]byte("401 Unauthorized"))
-						return
-					}
-
-					if !userinfo.IsAdmin() {
-						utils.SendErrorResponse(w, "Permission Denied")
+				adminRouter.HandleFunc("/system/disk/raid/addMemeber", func(w http.ResponseWriter, r *http.Request) {
+					if !AuthValidateSecureRequest(w, r) {
 						return
 					}
-
-					//Double check password for this user
-					password, err := utils.PostPara(r, "password")
-					if err != nil {
-						utils.SendErrorResponse(w, "Password Incorrect")
-						return
-					}
-
-					passwordCorrect, rejectionReason := authAgent.ValidateUsernameAndPasswordWithReason(userinfo.Username, password)
-					if !passwordCorrect {
-						utils.SendErrorResponse(w, rejectionReason)
+					raidManager.HandleAddDiskToRAIDVol(w, r)
+				})
+				adminRouter.HandleFunc("/system/disk/raid/removeMemeber", func(w http.ResponseWriter, r *http.Request) {
+					if !AuthValidateSecureRequest(w, r) {
 						return
 					}
-
-					//OK! Handle the remaining operations
 					raidManager.HandleRemoveDiskFromRAIDVol(w, r)
 				})
 

+ 1 - 1
main.flags.go

@@ -31,7 +31,7 @@ var subserviceBasePort = 12810            //Next subservice port
 
 // =========== SYSTEM BUILD INFORMATION ==============
 var build_version = "development"                      //System build flag, this can be either {development / production / stable}
-var internal_version = "0.2.019"                       //Internal build version, [fork_id].[major_release_no].[minor_release_no]
+var internal_version = "0.2.020"                       //Internal build version, [fork_id].[major_release_no].[minor_release_no]
 var deviceUUID string                                  //The device uuid of this host
 var deviceVendor = "IMUSLAB.INC"                       //Vendor of the system
 var deviceVendorURL = "http://imuslab.com"             //Vendor contact information

+ 56 - 1
mod/disk/raid/handler.go

@@ -39,6 +39,12 @@ func (m *Manager) HandleRemoveDiskFromRAIDVol(w http.ResponseWriter, r *http.Req
 		return
 	}
 
+	//Check if this is the only disk in the array
+	if !m.IsSafeToRemove(mdDev, sdXDev) {
+		utils.SendErrorResponse(w, "removal of this device will cause data loss")
+		return
+	}
+
 	//Check if the disk is already failed
 	diskAlreadyFailed, err := m.DiskIsFailed(mdDev, sdXDev)
 	if err != nil {
@@ -69,8 +75,57 @@ func (m *Manager) HandleRemoveDiskFromRAIDVol(w http.ResponseWriter, r *http.Req
 }
 
 func (m *Manager) HandleAddDiskToRAIDVol(w http.ResponseWriter, r *http.Request) {
-
 	//mdadm --add /dev/md0 /dev/sdb1
+	mdDev, err := utils.PostPara(r, "raidDev")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid raid device given")
+		return
+	}
+
+	sdXDev, err := utils.PostPara(r, "memDev")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid member device given")
+		return
+	}
+
+	//Check if target array exists
+	if !m.RAIDDeviceExists(mdDev) {
+		utils.SendErrorResponse(w, "target RAID array not exists")
+		return
+	}
+
+	//Check if disk already in another RAID array or mounted
+	isMounted, err := diskfs.DeviceIsMounted(sdXDev)
+	if err != nil {
+		utils.SendErrorResponse(w, "unable to read device state")
+		return
+	}
+
+	if isMounted {
+		utils.SendErrorResponse(w, "target device is mounted")
+		return
+	}
+
+	diskUsedByAnotherRAID, err := m.DiskIsUsedInAnotherRAIDVol(sdXDev)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+	}
+
+	if diskUsedByAnotherRAID {
+		utils.SendErrorResponse(w, "target device already been used by another RAID volume")
+		return
+	}
+
+	//OK! Add it to the target RAID array
+	err = m.AddDisk(mdDev, sdXDev)
+	if err != nil {
+		utils.SendErrorResponse(w, "adding disk to RAID volume failed")
+		return
+	}
+
+	log.Println("[RAID] Device " + sdXDev + " added to RAID volume " + mdDev)
+
+	utils.SendOK(w)
 }
 
 // Handle force flush reloading mdadm to solve the md0 become md127 problem

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

@@ -265,3 +265,20 @@ func (m *Manager) RemoveDisk(mdDevice, diskPath string) error {
 	}
 	return nil
 }
+
+// Add disk to a given RAID array, must be unmounted and not in-use
+func (m *Manager) AddDisk(mdDevice, diskPath string) error {
+	//mdadm commands require full path
+	if !strings.HasPrefix(diskPath, "/dev/") {
+		diskPath = filepath.Join("/dev/", diskPath)
+	}
+	if !strings.HasPrefix(mdDevice, "/dev/") {
+		mdDevice = filepath.Join("/dev/", mdDevice)
+	}
+
+	cmd := exec.Command("sudo", "mdadm", mdDevice, "--add", diskPath)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to add disk: %v", err)
+	}
+	return nil
+}

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

@@ -23,6 +23,59 @@ func GetNextAvailableMDDevice() (string, error) {
 	return "", fmt.Errorf("no available /dev/mdX devices found")
 }
 
+// Check if the given device is safe to remove from the array without losing data
+func (m *Manager) IsSafeToRemove(mdDev string, sdXDev string) bool {
+	targetRAIDVol, err := m.GetRAIDDeviceByDevicePath(mdDev)
+	if err != nil {
+		return false
+	}
+
+	//Trim off the /dev/ part if exists
+	sdXDev = filepath.Base(sdXDev)
+
+	//Check how many members left if this is removed
+	remainingMemebers := 0
+	for _, member := range targetRAIDVol.Members {
+		if member.Name != sdXDev {
+			remainingMemebers++
+		}
+	}
+
+	//Check if removal of sdX will cause data loss
+	if strings.EqualFold(targetRAIDVol.Level, "raid0") {
+		return false
+	} else if strings.EqualFold(targetRAIDVol.Level, "raid1") {
+		//In raid1, you need at least 1 disk to hold data
+		return remainingMemebers >= 1
+	} else if strings.EqualFold(targetRAIDVol.Level, "raid5") {
+		//In raid 5, at least 2 disk is needed before data loss
+		return remainingMemebers >= 2
+	} else if strings.EqualFold(targetRAIDVol.Level, "raid6") {
+		//In raid 6, you need 6 disks with max loss = 2 disks
+		return remainingMemebers >= 2
+	}
+
+	return true
+}
+
+// Check if the given disk (sdX) is currently used in any volume
+func (m *Manager) DiskIsUsedInAnotherRAIDVol(sdXDev string) (bool, error) {
+	raidPools, err := m.GetRAIDDevicesFromProcMDStat()
+	if err != nil {
+		return false, errors.New("unable to access RAID controller state")
+	}
+
+	for _, md := range raidPools {
+		for _, member := range md.Members {
+			if member.Name == filepath.Base(sdXDev) {
+				return true, nil
+			}
+		}
+	}
+
+	return false, nil
+}
+
 // ClearSuperblock clears the superblock of the specified disk so it can be used safely
 func (m *Manager) ClearSuperblock(devicePath string) error {
 	isMounted, err := diskfs.DeviceIsMounted(devicePath)

+ 15 - 0
web/SystemAO/disk/raid/img/drive-add.svg

@@ -0,0 +1,15 @@
+<?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" id="圖層_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">
+<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.729-1.357,3.127-3.034,3.127H17.535c-1.676,0-3.035-1.397-3.035-3.127V86.002
+	c0-1.729,1.359-3.127,3.035-3.127h92.556c1.677,0,3.034,1.398,3.034,3.127V105.811z"/>
+<circle fill="#00A0E9" cx="100.375" cy="95.916" r="3.708"/>
+<circle fill="#33B418" cx="102.083" cy="51.5" r="20"/>
+<g>
+	<path fill="#FFFFFF" d="M112.599,53.259h-8.796v8.756h-3.458v-8.756h-8.776v-3.498h8.776v-8.776h3.458v8.776h8.796V53.259z"/>
+</g>
+</svg>

+ 14 - 0
web/SystemAO/disk/raid/img/drive-format.svg

@@ -0,0 +1,14 @@
+<?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" id="圖層_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">
+<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.729-1.357,3.127-3.034,3.127H17.535c-1.676,0-3.035-1.396-3.035-3.127V86.002
+	c0-1.729,1.359-3.127,3.035-3.127h92.556c1.677,0,3.034,1.398,3.034,3.127V105.811z"/>
+<circle fill="#00A0E9" cx="100.375" cy="95.916" r="3.708"/>
+<path fill="#FFFFFF" d="M84.535,64.426c-5.24,5.244-14.758,4.226-21.258-2.275c-6.499-6.498-7.52-16.018-2.276-21.26
+	c5.242-5.24,14.761-4.221,21.258,2.276C88.76,49.667,89.781,59.186,84.535,64.426z"/>
+<rect x="66.415" y="19.963" transform="matrix(0.7071 -0.7071 0.7071 0.7071 0.1325 73.5299)" fill="#00A0E9" width="44.814" height="33.285"/>
+</svg>

+ 25 - 9
web/SystemAO/disk/raid/index.html

@@ -330,17 +330,20 @@
                 payload = encodeURIComponent(JSON.stringify(payload));
                 parent.newFloatWindow({
                     url: "SystemAO/disk/raid/newdisk.html#" + payload,
-                    width: 390,
-                    height: 620,
+                    width: 940,
+                    height: 480,
                     appicon: "SystemAO/disk/raid/img/raid.svg",
-                    title: `Add new disk to RAID`,
+                    title: `Add new disk to RAID volume ${arrayName}`,
                     parent: ao_module_windowID,
                     callback: "handleAddDiskCallback"
                 });
             }
 
             window.handleAddDiskCallback = function(data){
-
+                //Done adding disk
+                setTimeout(function(){
+                    reloadRAIDVolDetail();
+                }, 300);
             }
 
             /* Disk Remove Functions */
@@ -432,7 +435,7 @@
             //Reload the current raid volume detail
             function reloadRAIDVolDetail(){
                 if (raidManager.editingArray != undefined){
-                    loadRAIDVolDetail(raidManager.editingArray);
+                    initRAIDVolList(raidManager.editingArray);
                 } 
             }
 
@@ -449,6 +452,13 @@
                     return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
                 };
 
+                function uuidv4() {
+                    return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
+                        (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
+                    );
+                }
+
+
                 //Highlight the vol selected
                 $(".raidVol").removeClass('active');
                 $(".raidVol").each(function(){
@@ -535,7 +545,7 @@
                             }
 
                             //A random UID for this DOM element to make handling easier
-                            let raiddeviceUID = Date.now();
+                            let raiddeviceUID = uuidv4();
                             $("#raidDiskList").append(`<div class="ui basic segment raiddevice ${raiddeviceUID}" domid="${raiddeviceUID}">
                                 <h4 class="ui header">
                                     <img src="../disk/raid/img/${driveIcon}">
@@ -590,7 +600,7 @@
             }
 
             function resolveDiskLabelToDOM(diskPath, domSelector){
-                $.get("/system/disk/devices/model?devName=" + diskPath, function(data){
+                $.get("../../system/disk/devices/model?devName=" + diskPath, function(data){
                     let diskLabelName = ""
                     if (data.error == undefined){
                         //[0] is disk labeled name
@@ -602,7 +612,8 @@
             }
 
             //Initialize the RAID volume list on this system
-            function initRAIDVolList(){
+            //Give selectMDName for auto selecting a volume
+            function initRAIDVolList(selectMDName=""){
                 $("#raidVolList").html(`<p style="text-align: center;"><i class="ui loading spinner icon"></i></p>`);
                 $.get("../../system/disk/raid/list", function(data){
                     if (data.error != undefined){
@@ -636,7 +647,12 @@
                         $("#raidVolList").html(`<div><i class="ui green check circle icon"></i> No RAID Volumes</div>`);
                     }else{
                         //Load the first RAID vol info
-                        loadRAIDVolDetail(data[0].DevicePath);
+                        if (selectMDName){
+                            loadRAIDVolDetail(selectMDName);
+                        }else{
+                            loadRAIDVolDetail(data[0].DevicePath);
+                        }
+                        
                     }
                     $("#RAIDvolCount").text(data.length);
 

+ 278 - 2
web/SystemAO/disk/raid/newdisk.html

@@ -7,17 +7,293 @@
         <link rel="stylesheet" href="../../../script/semantic/semantic.min.css">
         <script type="text/javascript" src="../../../script/jquery.min.js"></script>
         <script type="text/javascript" src="../../../script/semantic/semantic.min.js"></script>
+        <script type="text/javascript" src="../../../script/ao_module.js"></script>
         <style>
-           
+            .installedDisk .checkboxWrapper{
+                position: absolute;
+                right: 1em;
+                top: 1.8em;
+            }
 
+            .installedDisk:not(.disabled){
+                cursor: pointer;
+            }
+            .installedDisk:not(.disabled):hover{
+                background-color: rgb(243, 243, 243);
+            }
+
+            .backicon {
+                position: fixed;
+                bottom: 0;
+                left: 0;
+                width: 30%;
+                height: calc(50vh - 10px);
+                background-image: url('./img/drive-add.svg');
+                background-size: contain; 
+                background-repeat: no-repeat; 
+                margin-left: -3%;
+                margin-bottom: -5%;
+                opacity: 0.2; 
+            }
+
+            .installedDisk.active{
+                background-color: rgb(232, 246, 253);
+            }
         </style>
     </head>
     <body>
         <br>
+        <div class="backicon"></div>
         <div class="ui container">
-        
+            <div style="float:right;">
+                <button class="ui circular icon basic button" onclick="reloadList();"><i class="ui green refresh icon"></i></button>
+                <button id="confirmButton" onclick="addDisk();" class="ui circular green icon button"><i class="ui check icon"></i></button>
+            </div>
+            <p>Select a disk to add to RAID Volume <span id="targetRAIDVol"></span></p>
+            <br>
+            <div id="noUsableDisk" class="ui yellow message" style="display:none;">
+                <i class="yellow exclamation triangle icon"></i> There are no usable / spare disks on your system
+            </div>
+            <div id="usablediskSelectionList" style="margin-bottom: 1em;">
+
+            </div>
+            <div id="disableddiskSelectionList">
+
+            </div>
         </div>
+
+        <!-- Disk Format Confirmation -->
+        <div id="confirmDiskChoice" class="ui mini modal">
+            <i class="close icon"></i>
+            <div class="header">
+              Confirm Disk Choice
+            </div>
+            <div class="image content">
+              <div class="ui medium image">
+                <img src="./img/drive-format.svg">
+              </div>
+              <div class="description">
+                <h3 id="oprconfirm"></h3>
+                <p><b>Selecting this disk will erase all data permanently.</b> Confirm?</p>
+              </div>
+            </div>
+            <div class="actions">
+              <div class="ui black deny button">
+                Cancel
+              </div>
+              <div class="ui positive left labeled icon button" onclick="confirmAddDisk();">
+                <i class="check icon"></i>
+                Confirm
+              </div>
+            </div>
+          </div>
+
+        <br><br>
         <script>
+            let selectedDiskID = "";
+            let editingMD = {
+                md: "" //RAID device path, e.g. /dev/md0
+            };
+            if (window.location.hash.length > 1){
+                var tmp = window.location.hash.substr(1);
+                editingMD = JSON.parse(decodeURIComponent(tmp));
+            }
+
+            if (editingMD.md == ""){
+                //invalid usage
+                alert("Invalid usage");
+            }else{
+                //load disk info
+                initDiskList();
+                $("#targetRAIDVol").text(editingMD.md);
+            }
+
+            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];
+            };
+
+            function uuidv4() {
+                return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
+                    (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
+                );
+            }
+
+            /* Disk Choice Confirmation */
+            function addDisk(){
+                selectedDiskID = $("input[type=radio][name=\"disk\"]:checked");
+                if (selectedDiskID.length == 0){
+                    //No selected disk
+                    return;
+                }
+                selectedDiskID = selectedDiskID[0].value;
+                $("#oprconfirm").text(selectedDiskID);
+                $("#confirmDiskChoice").modal("show");
+            }
+
+            function confirmAddDisk(){
+                var apiObject = {
+                    api: "../system/disk/raid/addMemeber",
+                    data: {
+                        raidDev:  editingMD.md,
+                        memDev: selectedDiskID
+                    },
+                    title: `<i class='yellow add circle icon'></i> Add disk to RAID volume `,
+                    desc: `Confirm formatting and adding ${selectedDiskID} to ${editingMD.md}`,
+                    thisuser: true, //This username as default, set to false for entering other user
+                    method: "POST",
+                    success: undefined
+                }
+                apiObject = encodeURIComponent(JSON.stringify(apiObject));
+        
+                parent.newFloatWindow({
+                    url: "SystemAO/security/authreq.html#" + apiObject,
+                    width: 480,
+                    height: 300,
+                    appicon: "SystemAO/security/img/lock.svg",
+                    title: `Confirm Disk Add`,
+                    parent: ao_module_windowID,
+                    callback: "handleDiskAddCallback"
+                });
+            }
+
+            window.handleDiskAddCallback = function(data){
+                //Disk added. Handle callback to parent 
+                if (ao_module_hasParentCallback()){
+                    ao_module_parentCallback(data);
+                }
+
+                //Operation completed. 
+                ao_module_close();
+            }
+
+
+            function reloadList(){
+                initDiskList();
+            }
+
+            function initDiskList(){
+                $("#disableddiskSelectionList").html("");
+                $("#usablediskSelectionList").html("");
+                $.get("../../../system/disk/devices/list", function(data){
+                    if (data.error != undefined){
+                        console.log(data.error);
+                    }else{
+                        let usableDiskCount = 0;
+                        data.forEach(function(driveInfo){
+                            //Generate the children table
+                            let childrenTable = "";
+                            let notUsable = false;
+                            if (driveInfo.children != undefined){
+                                for (var i = 0; i < driveInfo.children.length; i++){
+                                    let thisChild = driveInfo.children[i];
+                                    childrenTable = childrenTable + `<tr>
+                                        <td>${thisChild.name}</td>
+                                        <td>${bytesToSize(thisChild.size)}</td>
+                                        <td>${thisChild.type}</td>
+                                        <td>${thisChild.mountpoint}</td>
+                                    </tr>`;
+
+                                    //Check if this disk is already in other raid object
+                                    if (thisChild.name.substr(0, 2) == "md" || thisChild.type.substr(0, 4) == "raid"){
+                                        //This is already in some raid array
+                                        notUsable = true;
+                                    }
+
+                                    if (thisChild.mountpoint == "/"){
+                                        //This is the root drive! Don't allow self destruct
+                                        notUsable = true;
+                                    }
+                                }
+                            }
+                            
+                            var domUID = uuidv4();
+                            let diskDOM = (`<div onclick="handleSelect(this);" class="ui segment installedDisk ${domUID} ${notUsable?"disabled":""}">
+                                <h4 class="ui header">
+                                    <img src="./img/drive.svg">
+                                    <div class="content">
+                                        ${bytesToSize(driveInfo.size)} - <span class="diskname ${domUID}">Unknown Disk</span>
+                                        <div class="sub header">/dev/${driveInfo.name}</div>
+                                    </div>
+                                </h4>
+                                <div class="children">
+                                    <table class="ui very basic unstackable table">
+                                        <thead>
+                                        <tr>
+                                            <th>Name</th>
+                                            <th>Size</th>
+                                            <th>Partition Type</th>
+                                            <th>Mount Point</th>
+                                        </tr></thead>
+                                        <tbody>${childrenTable}</tbody>
+                                    </table>
+                                </div>
+                                <div class="checkboxWrapper">
+                                    <div class="ui radio checkbox">
+                                        <input type="radio" name="disk" value="/dev/${driveInfo.name}">
+                                        <label></label>
+                                    </div>
+                                </div>
+                            </div>`);
+                            
+                            if (!notUsable){
+                                $("#usablediskSelectionList").append(diskDOM);
+                            }else{
+                                $("#disableddiskSelectionList").append(diskDOM);
+                            }
+
+                            if (driveInfo.children == undefined || driveInfo.children.length == 0){
+                                $(".installedDisk." + domUID).find(".children").hide();
+                            }
+                            //console.log(driveInfo);
+
+                            if (notUsable){
+                                $(".installedDisk." + domUID).find(".checkboxWrapper").hide();
+                            }
+
+                            resolveDiskLabelToDOM(`/dev/${driveInfo.name}`, ".diskname." + domUID);
+
+                            if (!notUsable){
+                                usableDiskCount++;
+                            }
+                        });
+
+                        if (usableDiskCount == 0){
+                            //No usable disk. Is all disk in use?
+                            $("#noUsableDisk").show();
+                            $("#confirmButton").addClass("disabled");
+                        }else{
+                            $("#noUsableDisk").hide();
+                            $("#confirmButton").removeClass("disabled");
+                        }
+                    }
+                })
+            }
+
+            //Resolve the disk label name to dom
+            function resolveDiskLabelToDOM(diskPath, domSelector){
+                $.get("../../../system/disk/devices/model?devName=" + diskPath, function(data){
+                    let diskLabelName = ""
+                    if (data.error == undefined){
+                        //[0] is disk labeled name
+                        //[1] is disk labeled size
+                        diskLabelName = data[0];
+                    }
+                    $(domSelector).html(diskLabelName);
+                });
+            }
+
+            //Select the select box in the hdd
+            function handleSelect(object){
+                if (!$(object).hasClass("disabled")){
+                    $(object).find("input[type=radio]")[0].checked = true;
+                    $(".installedDisk.active").removeClass(".active");
+                    $(object).addClass('active');
+                }
+            }
             
         </script>
     </body>