界面优化
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace dy.net.utils
|
||||
{
|
||||
|
||||
|
||||
public static class DiskInfoHelper
|
||||
{/// <summary>
|
||||
/// 在Docker容器中获取Linux宿主机的本地固定磁盘总空间(GB)
|
||||
/// 前提:宿主机需挂载 /proc 到容器内的 /host/proc(启动时加 -v /proc:/host/proc:ro)
|
||||
/// </summary>
|
||||
/// <returns>总空间字符串(如 "1408.35 GB"),失败时返回错误信息</returns>
|
||||
public static string GetDockerHostTotalDiskSpaceGB()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 检查是否为Linux宿主机(Docker主要运行在Linux上)
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return "仅支持Linux宿主机(Docker容器内)";
|
||||
}
|
||||
|
||||
// 2. 检查宿主机/proc是否已挂载到容器
|
||||
string hostProcMountsPath = "/app/db/mounts";
|
||||
if (!File.Exists(hostProcMountsPath))
|
||||
{
|
||||
return "未检测到宿主机/proc挂载,请使用 -v /proc:/host/proc:ro 启动容器";
|
||||
}
|
||||
|
||||
// 3. 从宿主机/proc/mounts筛选物理磁盘挂载点(排除虚拟文件系统)
|
||||
var physicalMounts = GetHostPhysicalMounts(hostProcMountsPath);
|
||||
if (!physicalMounts.Any())
|
||||
{
|
||||
return "未找到宿主机的物理磁盘挂载点";
|
||||
}
|
||||
|
||||
// 4. 计算所有物理磁盘的总空间(通过df命令获取挂载点的总空间)
|
||||
long totalBytes = 0;
|
||||
foreach (var mountPoint in physicalMounts)
|
||||
{
|
||||
// 执行df命令获取宿主机挂载点的总空间(需容器内有df工具,或通过/proc/diskstats计算)
|
||||
var (success, bytes) = GetMountPointTotalBytes(mountPoint);
|
||||
if (success)
|
||||
{
|
||||
totalBytes += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalBytes == 0)
|
||||
{
|
||||
return "无法读取宿主机磁盘空间(可能权限不足)";
|
||||
}
|
||||
|
||||
// 5. 转换为GB
|
||||
return ConvertBytesToGb(totalBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"获取宿主机磁盘空间失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从宿主机/proc/mounts筛选物理磁盘挂载点(排除虚拟文件系统)
|
||||
/// </summary>
|
||||
private static List<string> GetHostPhysicalMounts(string hostProcMountsPath)
|
||||
{
|
||||
var physicalMounts = new List<string>();
|
||||
// 虚拟文件系统类型(排除这些类型,剩下的视为物理磁盘相关)
|
||||
var virtualFsTypes = new HashSet<string>
|
||||
{
|
||||
"tmpfs", "sysfs", "proc", "devtmpfs", "devpts", "cgroup", "cgroup2",
|
||||
"securityfs", "pstore", "debugfs", "hugetlbfs", "mqueue", "configfs",
|
||||
"fusectl", "overlay", "squashfs", "overlay2" // overlay/overlay2是Docker自身的存储驱动,需排除
|
||||
};
|
||||
|
||||
foreach (var line in File.ReadAllLines(hostProcMountsPath))
|
||||
{
|
||||
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
string device = parts[0]; // 设备名(如/dev/sda1)
|
||||
string mountPoint = parts[1]; // 挂载点(如/)
|
||||
string fsType = parts[2]; // 文件系统类型
|
||||
|
||||
// 筛选条件:非虚拟文件系统 + 设备名以/dev/开头(物理设备)
|
||||
if (!virtualFsTypes.Contains(fsType) && device.StartsWith("/dev/"))
|
||||
{
|
||||
physicalMounts.Add(mountPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return physicalMounts.Distinct().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过df命令获取宿主机挂载点的总空间(字节)
|
||||
/// (需容器内安装coreutils,或替换为解析/proc/diskstats的逻辑)
|
||||
/// </summary>
|
||||
private static (bool success, long totalBytes) GetMountPointTotalBytes(string hostMountPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 在容器内执行df命令,指定宿主机的挂载点(需宿主机路径在容器内可见,或通过/proc计算)
|
||||
// 注意:df命令返回的是1K-blocks,需转换为字节(*1024)
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "df",
|
||||
Arguments = $"-P {hostMountPoint}", // -P 确保输出格式一致
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
string output = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
// 解析df输出(第二行为数据行)
|
||||
var lines = output.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (lines.Length < 2)
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
var dataParts = lines[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (dataParts.Length >= 2 && long.TryParse(dataParts[1], out long blocks))
|
||||
{
|
||||
return (true, blocks * 1024); // 1K-blocks -> 字节
|
||||
}
|
||||
|
||||
return (false, 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字节转GB(1GB = 1024^3字节)
|
||||
/// </summary>
|
||||
private static string ConvertBytesToGb(long bytes)
|
||||
{
|
||||
if (bytes <= 0) return "磁盘空间计算错误";
|
||||
double gb = (double)bytes / (1024 * 1024 * 1024);
|
||||
return $"{gb:F2} GB";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user