|
@@ -0,0 +1,254 @@
|
|
|
+<div class="ts-content">
|
|
|
+ <div class="ts-container is-padded">
|
|
|
+ <div class="ts-grid mobile:is-stacked">
|
|
|
+ <div class="column is-6-wide">
|
|
|
+ <div id="raid_array_list" class="ts-menu is-start-icon is-separated">
|
|
|
+ <a class="item">
|
|
|
+ <span class="ts-icon is-user-icon"></span> 使用者
|
|
|
+ </a>
|
|
|
+ <a class="item is-active">
|
|
|
+ <span class="ts-icon is-house-icon"></span> 首頁
|
|
|
+ </a>
|
|
|
+ <a class="item">
|
|
|
+ <span class="ts-icon is-newspaper-icon"></span> 新聞
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+ <div class="ts-divider has-top-spaced-small"></div>
|
|
|
+ <div class="ts-content is-center-aligned">
|
|
|
+ <button class="ts-button is-start-icon is-positive is-circular">
|
|
|
+ <span class="ts-icon is-circle-plus-icon" style="color: var(--ts-positive-500);"></span>
|
|
|
+ <span i18n>Create RAID
|
|
|
+ // 新增陣列
|
|
|
+ </span>
|
|
|
+ </button>
|
|
|
+ <button class="ts-button is-start-icon is-positive is-circular">
|
|
|
+ <span class="ts-icon is-rotate-icon" style="color: var(--ts-primary-500);"></span>
|
|
|
+ <span i18n>Assemble
|
|
|
+ // 重組陣列
|
|
|
+ </span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="column is-fluid">
|
|
|
+ <div id="raid_details">
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+<script>
|
|
|
+
|
|
|
+
|
|
|
+ function initRAIDDeviceList(){
|
|
|
+ // Utility function to convert bytes to human-readable format
|
|
|
+ function bytesToHumanReadable(bytes) {
|
|
|
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
|
+ if (bytes === 0) return '0 B';
|
|
|
+ const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
|
|
+ return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
|
|
|
+ }
|
|
|
+ $.ajax({
|
|
|
+ url: './api/raid/list',
|
|
|
+ type: 'GET',
|
|
|
+ dataType: 'json',
|
|
|
+ success: function(data) {
|
|
|
+ $('#raid_array_list').html("");
|
|
|
+ if (data.error != undefined){
|
|
|
+ // Handle error response
|
|
|
+ console.error('Error fetching RAID devices:', data.error);
|
|
|
+ $('#raid_array_list').append('<div class="ts-text is-error">Error: ' + data.error + '</div>');
|
|
|
+ }else{
|
|
|
+ let isSyncing = false;
|
|
|
+ data.forEach((raid, index) => {
|
|
|
+ // Add a new menu item for each RAID array
|
|
|
+ let icon = '';
|
|
|
+ if (raid.State.includes('clean') && !raid.State.includes('sync')) {
|
|
|
+ icon = '<span class="ts-icon is-check-icon" style="color: var(--ts-positive-500);"></span>';
|
|
|
+ } else if (raid.State.includes('sync')) {
|
|
|
+ isSyncing = true;
|
|
|
+ icon = '<span class="ts-icon is-spinning is-rotate-icon" style="color: var(--ts-positive-500);"></span>';
|
|
|
+ } else if (raid.State.includes('degraded')) {
|
|
|
+ icon = '<span class="ts-icon is-triangle-exclamation-icon" style="color: var(--ts-warning-600);"></span>';
|
|
|
+ } else if (raid.State.includes('fail')) {
|
|
|
+ icon = '<span class="ts-icon is-circle-xmark-icon" style="color: var(--ts-negative-500);"></span>';
|
|
|
+ } else {
|
|
|
+ icon = '<span class="ts-icon is-question-icon" style="color: var(--ts-gray-500);"></span>';
|
|
|
+ }
|
|
|
+ const menuItem = `
|
|
|
+ <a class="raid-array item ${index==0?'is-active':''}" id="raid_menu_${index}" onclick="showRAIDDetails(${index})">
|
|
|
+ ${icon}
|
|
|
+ <div class="ts-content is-dense">
|
|
|
+ <div>
|
|
|
+ <span class="ts-text is-heavy">${raid.DevicePath}</span> | ${raid.RaidLevel.toUpperCase()}
|
|
|
+ </div>
|
|
|
+ <div class="ts-text is-tiny has-top-spaced-small">
|
|
|
+ ${raid.Name}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </a>
|
|
|
+ `;
|
|
|
+ $('#raid_array_list').append(menuItem);
|
|
|
+
|
|
|
+ // Add a hidden div for each RAID array's details
|
|
|
+ const raidDetails = `
|
|
|
+ <div id="raid_details_${index}" class="raid-details" style="display: none ;">
|
|
|
+ <div class="ts-box">
|
|
|
+ <div class="ts-content is-padded">
|
|
|
+ <div class="ts-header is-start-icon">
|
|
|
+ ${icon}
|
|
|
+ ${raid.DevicePath} | ${raid.RaidLevel.toUpperCase()}
|
|
|
+ </div>
|
|
|
+ <div class="ts-text is-description">
|
|
|
+ ${raid.UUID}<br>
|
|
|
+ ${raid.Name}
|
|
|
+ </div>
|
|
|
+ <div class="ts-text">
|
|
|
+ <span i18n> State
|
|
|
+ // 狀態
|
|
|
+ </span>: ${raid.State}<br>
|
|
|
+ <div class="sync-progress has-top-spaced-small ${isSyncing?'need-update-raid-sync-progress':''}" devname="${raid.DevicePath}" style="display: ${isSyncing?"auto":"none"};">
|
|
|
+ <div class="ts-progress is-processing">
|
|
|
+ <div class="bar" style="--value: 0">
|
|
|
+ <div class="text">0%</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="ts-text is-description has-top-spaced-small">
|
|
|
+ <span i18n> Synchronized
|
|
|
+ // 已處理</span>
|
|
|
+ <span class="processed_blocks"></span>
|
|
|
+ <span>/</span>
|
|
|
+ <span class="total_blocks"></span>
|
|
|
+ <span i18n> blocks
|
|
|
+ // 個區塊
|
|
|
+ </span><br>
|
|
|
+ <!-- <span i18n> Speed
|
|
|
+ // 速度
|
|
|
+ </span>: <span class="speed"></span><br>
|
|
|
+ <span i18n> Expected Time
|
|
|
+ // 預估時間
|
|
|
+ </span>: <span class="expected_time"></span>
|
|
|
+ -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <span i18n> Array Size
|
|
|
+ // 陣列大小
|
|
|
+ </span>: ${bytesToHumanReadable(raid.ArraySize * 1024)}<br>
|
|
|
+ <span i18n> Created
|
|
|
+ // 建立時間
|
|
|
+ </span>: <span>${new Date(raid.CreationTime).toLocaleString()}</span><br>
|
|
|
+ </div>
|
|
|
+ <table class="ts-table is-single-line has-top-spaced-large">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th i18n>Disk Status
|
|
|
+ // 磁碟狀態
|
|
|
+ </th>
|
|
|
+ <th i18n>Counts
|
|
|
+ // 數量
|
|
|
+ </th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr>
|
|
|
+ <td i18n> Active Devices
|
|
|
+ // 啟用的磁碟
|
|
|
+ </td>
|
|
|
+ <td>${raid.ActiveDevices}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td i18n> Working Devices
|
|
|
+ // 工作中的磁碟
|
|
|
+ </td>
|
|
|
+ <td>${raid.WorkingDevices}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td i18n> Failed Devices
|
|
|
+ // 故障的磁碟
|
|
|
+ </td>
|
|
|
+ <td>${raid.FailedDevices}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td i18n> Spare Devices
|
|
|
+ // 備用磁碟
|
|
|
+ </td>
|
|
|
+ <td>${raid.SpareDevices}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="ts-box is-padded has-top-spaced-small">
|
|
|
+ <div class="ts-content">
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ $('#raid_details').append(raidDetails);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ relocale(); // Recalculate layout
|
|
|
+ syncProgressTicker(); // Start the sync progress ticker
|
|
|
+ },
|
|
|
+ error: function(xhr, status, error) {
|
|
|
+ console.error('Error fetching RAID devices:', error);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ initRAIDDeviceList();
|
|
|
+
|
|
|
+ //Create a ticker to check for RAID sync progress
|
|
|
+ function syncProgressTicker(){
|
|
|
+ let syncProgressTracker = $(".need-update-raid-sync-progress");
|
|
|
+ if (syncProgressTracker.length > 0){
|
|
|
+ syncProgressTracker.each(function(){
|
|
|
+ let devname = $(this).attr("devname");
|
|
|
+ $.ajax({
|
|
|
+ url: './api/raid/sync?dev=' + devname,
|
|
|
+ type: 'GET',
|
|
|
+ dataType: 'json',
|
|
|
+ data: { devname: devname },
|
|
|
+ success: function(data) {
|
|
|
+ if (data.error != undefined){
|
|
|
+ // Handle error response
|
|
|
+ console.error('Error fetching RAID sync progress:', data.error);
|
|
|
+ //Refresh the RAID list
|
|
|
+ initRAIDDeviceList();
|
|
|
+ }else{
|
|
|
+ let progress = parseFloat(data.ResyncPercent);
|
|
|
+ let total_blocks = parseInt(data.TotalBlocks);
|
|
|
+ let processed_blocks = parseInt(data.CompletedBlocks);
|
|
|
+ let expected_time = data.ExpectedTime;
|
|
|
+ let speed = data.Speed;
|
|
|
+
|
|
|
+ $(`.sync-progress[devname="${devname}"] .bar`).css('--value', progress);
|
|
|
+ $(`.sync-progress[devname="${devname}"] .bar .text`).text(`${progress.toFixed(1)}%`);
|
|
|
+ $(`.sync-progress[devname="${devname}"] .processed_blocks`).text(processed_blocks);
|
|
|
+ $(`.sync-progress[devname="${devname}"] .total_blocks`).text(total_blocks);
|
|
|
+ //$(`.sync-progress[devname="${devname}"] .ts-text.is-description .speed`).text(speed);
|
|
|
+ //$(`.sync-progress[devname="${devname}"] .ts-text.is-description .expected_time`).text(expected_time);
|
|
|
+
|
|
|
+ }
|
|
|
+ },
|
|
|
+ error: function(xhr, status, error) {
|
|
|
+ console.error('Error fetching RAID sync progress:', error);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ setInterval(syncProgressTicker, 5000); // Check every 5 seconds
|
|
|
+
|
|
|
+ function showRAIDDetails(index) {
|
|
|
+ $('.raid-details').hide(); // Hide all RAID details
|
|
|
+ $(`#raid_details_${index}`).show(); // Show the selected RAID details
|
|
|
+ $('.raid-array.is-active').removeClass('is-active'); // Remove active class from all menu items
|
|
|
+ $(`#raid_menu_${index}`).addClass('is-active'); // Add active class to the selected menu item
|
|
|
+ relocale(); // Recalculate layout
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|