增加操作系统显示,以及磁盘信息显示
This commit is contained in:
BIN
backend/backend
BIN
backend/backend
Binary file not shown.
@@ -94,12 +94,16 @@ type ProcessMetrics struct {
|
||||
|
||||
// DiskDetailMetrics 磁盘详细信息
|
||||
type DiskDetailMetrics struct {
|
||||
ID string `json:"id"` // 磁盘唯一ID(数字序列)
|
||||
Path string `json:"path"` // 设备路径
|
||||
Status string `json:"status"` // 设备状态
|
||||
Type string `json:"type"` // 设备类型
|
||||
SizeGB float64 `json:"size_gb"` // 设备大小(GB)
|
||||
Model string `json:"model"` // 设备型号
|
||||
Vendor string `json:"vendor"` // 设备厂商
|
||||
InterfaceType string `json:"interface_type"` // 接口类型
|
||||
FileSystem string `json:"file_system"` // 文件系统
|
||||
DiskUUID string `json:"disk_uuid"` // 磁盘UUID
|
||||
Description string `json:"description"` // 设备描述
|
||||
}
|
||||
|
||||
@@ -147,6 +151,7 @@ type MemoryInfo struct {
|
||||
|
||||
// NetworkHardwareInfo 网卡硬件信息
|
||||
type NetworkHardwareInfo struct {
|
||||
ID string `json:"id"` // 网卡唯一ID(数字序列)
|
||||
Name string `json:"name"` // 网卡名称
|
||||
MAC string `json:"mac"` // MAC地址
|
||||
IPAddresses []string `json:"ip_addresses"` // IP地址列表
|
||||
@@ -367,7 +372,7 @@ func HandleMetricsPost(c *gin.Context) {
|
||||
|
||||
// 写入磁盘详细信息
|
||||
for _, diskDetail := range req.DiskDetails {
|
||||
if err := globalStorage.WriteDiskDetailMetric(writeCtx, deviceID, diskDetail.Path, diskDetail.Status, diskDetail.Type, diskDetail.SizeGB, diskDetail.Model, diskDetail.InterfaceType, diskDetail.Description, baseTags); err != nil {
|
||||
if err := globalStorage.WriteDiskDetailMetric(writeCtx, deviceID, diskDetail.Path, diskDetail.Status, diskDetail.Type, diskDetail.SizeGB, diskDetail.Model, diskDetail.Vendor, diskDetail.InterfaceType, diskDetail.FileSystem, diskDetail.DiskUUID, diskDetail.Description, baseTags); err != nil {
|
||||
// 只记录警告,不影响后续指标处理
|
||||
log.Printf("Warning: Failed to write disk details for device %s: %v", diskDetail.Path, err)
|
||||
}
|
||||
@@ -421,12 +426,16 @@ func HandleMetricsPost(c *gin.Context) {
|
||||
// 磁盘详细信息
|
||||
for i, diskDetail := range req.Hardware.DiskDetails {
|
||||
diskData := map[string]interface{}{
|
||||
"id": diskDetail.ID,
|
||||
"path": diskDetail.Path,
|
||||
"status": diskDetail.Status,
|
||||
"type": diskDetail.Type,
|
||||
"size_gb": diskDetail.SizeGB,
|
||||
"model": diskDetail.Model,
|
||||
"vendor": diskDetail.Vendor,
|
||||
"interface_type": diskDetail.InterfaceType,
|
||||
"file_system": diskDetail.FileSystem,
|
||||
"disk_uuid": diskDetail.DiskUUID,
|
||||
"description": diskDetail.Description,
|
||||
"index": i,
|
||||
}
|
||||
@@ -438,6 +447,7 @@ func HandleMetricsPost(c *gin.Context) {
|
||||
// 网络网卡信息
|
||||
for i, netCard := range req.Hardware.NetworkCards {
|
||||
netData := map[string]interface{}{
|
||||
"id": netCard.ID,
|
||||
"name": netCard.Name,
|
||||
"mac": netCard.MAC,
|
||||
"mtu": netCard.MTU,
|
||||
@@ -1362,12 +1372,16 @@ func GetHardwareMetrics(c *gin.Context) {
|
||||
diskDetails := []map[string]interface{}{}
|
||||
for _, diskData := range diskDataList {
|
||||
diskDetail := map[string]interface{}{
|
||||
"id": diskData["id"],
|
||||
"path": diskData["path"],
|
||||
"status": diskData["status"],
|
||||
"type": diskData["type"],
|
||||
"size_gb": diskData["size_gb"],
|
||||
"model": diskData["model"],
|
||||
"vendor": diskData["vendor"],
|
||||
"interface_type": diskData["interface_type"],
|
||||
"file_system": diskData["file_system"],
|
||||
"disk_uuid": diskData["disk_uuid"],
|
||||
"description": diskData["description"],
|
||||
}
|
||||
diskDetails = append(diskDetails, diskDetail)
|
||||
@@ -1377,12 +1391,16 @@ func GetHardwareMetrics(c *gin.Context) {
|
||||
// 如果是单个磁盘
|
||||
diskDetails := []map[string]interface{}{
|
||||
{
|
||||
"id": diskData["id"],
|
||||
"path": diskData["path"],
|
||||
"status": diskData["status"],
|
||||
"type": diskData["type"],
|
||||
"size_gb": diskData["size_gb"],
|
||||
"model": diskData["model"],
|
||||
"vendor": diskData["vendor"],
|
||||
"interface_type": diskData["interface_type"],
|
||||
"file_system": diskData["file_system"],
|
||||
"disk_uuid": diskData["disk_uuid"],
|
||||
"description": diskData["description"],
|
||||
},
|
||||
}
|
||||
@@ -1405,6 +1423,7 @@ func GetHardwareMetrics(c *gin.Context) {
|
||||
}
|
||||
|
||||
networkCard := map[string]interface{}{
|
||||
"id": networkData["id"],
|
||||
"name": networkData["name"],
|
||||
"mac": networkData["mac"],
|
||||
"ip_addresses": ipAddresses,
|
||||
@@ -1428,6 +1447,7 @@ func GetHardwareMetrics(c *gin.Context) {
|
||||
}
|
||||
|
||||
networkCard := map[string]interface{}{
|
||||
"id": networkData["id"],
|
||||
"name": networkData["name"],
|
||||
"mac": networkData["mac"],
|
||||
"ip_addresses": ipAddresses,
|
||||
|
||||
@@ -210,7 +210,7 @@ func (s *Storage) WriteProcessMetric(ctx context.Context, deviceID string, proce
|
||||
}
|
||||
|
||||
// WriteDiskDetailMetric 写入磁盘详细信息
|
||||
func (s *Storage) WriteDiskDetailMetric(ctx context.Context, deviceID, diskPath, status, diskType string, sizeGB float64, model, interfaceType, description string, tags map[string]string) error {
|
||||
func (s *Storage) WriteDiskDetailMetric(ctx context.Context, deviceID, diskPath, status, diskType string, sizeGB float64, model, vendor, interfaceType, fileSystem, diskUUID, description string, tags map[string]string) error {
|
||||
// 创建标签映射,合并原有标签和新标签
|
||||
allTags := make(map[string]string)
|
||||
// 复制原有标签
|
||||
@@ -224,11 +224,14 @@ func (s *Storage) WriteDiskDetailMetric(ctx context.Context, deviceID, diskPath,
|
||||
allTags["status"] = status
|
||||
allTags["type"] = diskType
|
||||
allTags["model"] = model
|
||||
allTags["vendor"] = vendor
|
||||
allTags["interface_type"] = interfaceType
|
||||
allTags["file_system"] = fileSystem
|
||||
|
||||
// 创建字段映射
|
||||
fields := map[string]interface{}{
|
||||
"size_gb": sizeGB,
|
||||
"disk_uuid": diskUUID,
|
||||
"description": description,
|
||||
}
|
||||
|
||||
@@ -476,13 +479,24 @@ func (s *Storage) QueryHardwareMetrics(ctx context.Context, deviceID string) (ma
|
||||
|
||||
// 为每个硬件类型单独查询,避免类型冲突
|
||||
for _, hardwareType := range hardwareTypes {
|
||||
// 构建查询语句,获取设备最新的特定类型硬件信息
|
||||
query := `from(bucket: "` + s.bucket + `")
|
||||
|> range(start: -24h)
|
||||
|> filter(fn: (r) => r["_measurement"] == "hardware")
|
||||
|> filter(fn: (r) => r["device_id"] == "` + deviceID + `")
|
||||
|> filter(fn: (r) => r["type"] == "` + hardwareType + `")
|
||||
|> last()`
|
||||
// 构建查询语句
|
||||
var query string
|
||||
if hardwareType == "os" || hardwareType == "cpu" || hardwareType == "memory" {
|
||||
// 对于os、cpu、memory类型,我们只需要一个结果
|
||||
query = `from(bucket: "` + s.bucket + `")
|
||||
|> range(start: -24h)
|
||||
|> filter(fn: (r) => r["_measurement"] == "hardware")
|
||||
|> filter(fn: (r) => r["device_id"] == "` + deviceID + `")
|
||||
|> filter(fn: (r) => r["type"] == "` + hardwareType + `")
|
||||
|> last()`
|
||||
} else {
|
||||
// 对于disk和network类型,我们需要获取所有设备的所有字段记录
|
||||
query = `from(bucket: "` + s.bucket + `")
|
||||
|> range(start: -24h)
|
||||
|> filter(fn: (r) => r["_measurement"] == "hardware")
|
||||
|> filter(fn: (r) => r["device_id"] == "` + deviceID + `")
|
||||
|> filter(fn: (r) => r["type"] == "` + hardwareType + `")`
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
queryResult, err := queryAPI.Query(ctx, query)
|
||||
@@ -511,10 +525,12 @@ func (s *Storage) QueryHardwareMetrics(ctx context.Context, deviceID string) (ma
|
||||
hardwareInfo[hardwareType] = fieldMap
|
||||
}
|
||||
} else {
|
||||
// 对于disk和network类型,我们可能有多个结果
|
||||
// 我们需要收集所有结果,并按index排序
|
||||
// 使用map来去重
|
||||
resultMap := make(map[string]map[string]interface{})
|
||||
// 对于disk和network类型,我们可能有多个结果,每个字段作为单独的行返回
|
||||
var allResults []map[string]interface{}
|
||||
|
||||
// 创建一个map来存储每个设备的所有字段值
|
||||
// key是设备ID或index
|
||||
deviceFields := make(map[string]map[string]interface{})
|
||||
|
||||
// 遍历所有结果行,收集所有结果
|
||||
for queryResult.Next() {
|
||||
@@ -525,35 +541,57 @@ func (s *Storage) QueryHardwareMetrics(ctx context.Context, deviceID string) (ma
|
||||
fieldName := record.Field()
|
||||
fieldValue := record.Value()
|
||||
|
||||
// 获取硬件ID(用于去重)
|
||||
hardwareID := "unknown"
|
||||
if hardwareType == "disk" {
|
||||
// 对于磁盘,使用device_id作为唯一标识
|
||||
if devID, ok := record.ValueByKey("device_id").(string); ok {
|
||||
hardwareID = devID
|
||||
}
|
||||
// 获取设备唯一标识
|
||||
deviceKey := ""
|
||||
// 优先使用id字段作为设备标识
|
||||
if id, isString := fieldValue.(string); fieldName == "id" && isString {
|
||||
deviceKey = id
|
||||
// 创建设备字段映射
|
||||
deviceFields[deviceKey] = make(map[string]interface{})
|
||||
deviceFields[deviceKey]["id"] = id
|
||||
} else {
|
||||
// 对于网卡,使用name作为唯一标识
|
||||
if name, ok := record.ValueByKey("name").(string); ok {
|
||||
hardwareID = name
|
||||
// 否则,查找已存在的设备映射
|
||||
// 遍历所有设备映射,查找当前记录对应的设备
|
||||
for key, fields := range deviceFields {
|
||||
// 如果设备映射中没有id字段,或者id字段为空,跳过
|
||||
if id, ok := fields["id"].(string); ok && id != "" {
|
||||
// 假设当前记录属于该设备
|
||||
deviceKey = key
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取或创建硬件数据映射
|
||||
if _, ok := resultMap[hardwareID]; !ok {
|
||||
resultMap[hardwareID] = make(map[string]interface{})
|
||||
// 如果没有找到设备标识,使用index作为设备标识
|
||||
if deviceKey == "" {
|
||||
index := 0
|
||||
if idx, ok := record.ValueByKey("index").(int); ok {
|
||||
index = idx
|
||||
}
|
||||
deviceKey = fmt.Sprintf("%d", index)
|
||||
// 创建设备字段映射
|
||||
deviceFields[deviceKey] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// 添加字段值
|
||||
resultMap[hardwareID][fieldName] = fieldValue
|
||||
// 添加字段值到设备字段映射中
|
||||
deviceFields[deviceKey][fieldName] = fieldValue
|
||||
}
|
||||
|
||||
// 将去重后的结果转换为切片
|
||||
var allResults []map[string]interface{}
|
||||
for _, result := range resultMap {
|
||||
allResults = append(allResults, result)
|
||||
// 将deviceFields转换为切片
|
||||
for _, fields := range deviceFields {
|
||||
allResults = append(allResults, fields)
|
||||
}
|
||||
|
||||
// 按id排序
|
||||
sort.Slice(allResults, func(i, j int) bool {
|
||||
iID, iOk := allResults[i]["id"].(string)
|
||||
jID, jOk := allResults[j]["id"].(string)
|
||||
if iOk && jOk {
|
||||
return iID < jID
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// 如果有结果,添加到硬件信息结果中
|
||||
if len(allResults) > 0 {
|
||||
hardwareInfo[hardwareType] = allResults
|
||||
@@ -827,7 +865,9 @@ func (s *Storage) QueryDiskDetails(ctx context.Context, deviceID string, startTi
|
||||
"type": record.ValueByKey("type"),
|
||||
"size_gb": record.ValueByKey("size_gb"),
|
||||
"model": record.ValueByKey("model"),
|
||||
"vendor": record.ValueByKey("vendor"),
|
||||
"interface_type": record.ValueByKey("interface_type"),
|
||||
"file_system": record.ValueByKey("file_system"),
|
||||
"description": record.ValueByKey("description"),
|
||||
"agent_name": record.ValueByKey("agent_name"),
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1578,17 +1578,27 @@ async function loadServerInfo(deviceId) {
|
||||
// 将设备ID存储到全局状态
|
||||
state.currentDeviceID = deviceId;
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/devices/${deviceId}`);
|
||||
if (!response.ok) {
|
||||
// 获取服务器基本信息
|
||||
const deviceResponse = await fetch(`${API_BASE_URL}/devices/${deviceId}`);
|
||||
if (!deviceResponse.ok) {
|
||||
throw new Error('Failed to fetch server info');
|
||||
}
|
||||
const data = await response.json();
|
||||
const deviceData = data.device;
|
||||
const deviceData = await deviceResponse.json();
|
||||
|
||||
// 获取硬件信息,特别是操作系统信息
|
||||
const hardwareResponse = await fetch(`${API_BASE_URL}/metrics/hardware?device_id=${deviceId}`);
|
||||
let osFullname = '未知';
|
||||
if (hardwareResponse.ok) {
|
||||
const hardwareData = await hardwareResponse.json();
|
||||
if (hardwareData && hardwareData.hardware && hardwareData.hardware.os) {
|
||||
osFullname = hardwareData.hardware.os.fullname || '未知';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新服务器信息显示
|
||||
const serverInfoDisplay = document.getElementById('serverInfoDisplay');
|
||||
if (serverInfoDisplay) {
|
||||
serverInfoDisplay.innerHTML = `<p>服务器名称: <strong>${deviceData.name || deviceId}</strong> | IP地址: <strong>${deviceData.ip || '未知'}</strong></p>`;
|
||||
serverInfoDisplay.innerHTML = `<p>服务器名称: <strong>${deviceData.device.name || deviceId}</strong> | IP地址: <strong>${deviceData.device.ip || '未知'}</strong> | 操作系统: <strong>${osFullname}</strong></p>`;
|
||||
}
|
||||
|
||||
// 初始化状态卡片
|
||||
@@ -2807,9 +2817,6 @@ function bindEvents() {
|
||||
const currentTimeRangeDisplay = document.getElementById('currentTimeRangeDisplay');
|
||||
|
||||
if (zoomOutBtn && zoomInBtn && currentTimeRangeDisplay) {
|
||||
// 时间范围选项列表,用于缩放
|
||||
const timeRanges = ['30m', '1h', '2h', '6h', '12h', '24h'];
|
||||
}
|
||||
|
||||
// 网卡选择和刷新按钮事件
|
||||
const networkInterfaceSelect = document.getElementById('networkInterface');
|
||||
@@ -2837,6 +2844,7 @@ function bindEvents() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载网卡列表
|
||||
@@ -2949,6 +2957,9 @@ const updateTimeRangeDisplay = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 时间范围选项列表,用于缩放
|
||||
const timeRanges = ['30m', '1h', '2h', '6h', '12h', '24h'];
|
||||
|
||||
// 初始化显示
|
||||
updateTimeRangeDisplay();
|
||||
|
||||
@@ -2976,11 +2987,26 @@ const zoomInHandler = () => {
|
||||
};
|
||||
|
||||
// 为所有放大按钮添加事件监听器
|
||||
document.getElementById('cpuZoomInBtn')?.addEventListener('click', zoomInHandler);
|
||||
document.getElementById('memoryZoomInBtn')?.addEventListener('click', zoomInHandler);
|
||||
document.getElementById('diskZoomInBtn')?.addEventListener('click', zoomInHandler);
|
||||
document.getElementById('networkZoomInBtn')?.addEventListener('click', zoomInHandler);
|
||||
document.getElementById('speedZoomInBtn')?.addEventListener('click', zoomInHandler);
|
||||
const cpuZoomInBtn = document.getElementById('cpuZoomInBtn');
|
||||
if (cpuZoomInBtn) {
|
||||
cpuZoomInBtn.addEventListener('click', zoomInHandler);
|
||||
}
|
||||
const memoryZoomInBtn = document.getElementById('memoryZoomInBtn');
|
||||
if (memoryZoomInBtn) {
|
||||
memoryZoomInBtn.addEventListener('click', zoomInHandler);
|
||||
}
|
||||
const diskZoomInBtn = document.getElementById('diskZoomInBtn');
|
||||
if (diskZoomInBtn) {
|
||||
diskZoomInBtn.addEventListener('click', zoomInHandler);
|
||||
}
|
||||
const networkZoomInBtn = document.getElementById('networkZoomInBtn');
|
||||
if (networkZoomInBtn) {
|
||||
networkZoomInBtn.addEventListener('click', zoomInHandler);
|
||||
}
|
||||
const speedZoomInBtn = document.getElementById('speedZoomInBtn');
|
||||
if (speedZoomInBtn) {
|
||||
speedZoomInBtn.addEventListener('click', zoomInHandler);
|
||||
}
|
||||
|
||||
// 缩小事件
|
||||
const zoomOutHandler = () => {
|
||||
@@ -3006,11 +3032,26 @@ const zoomOutHandler = () => {
|
||||
};
|
||||
|
||||
// 为所有缩小按钮添加事件监听器
|
||||
document.getElementById('cpuZoomOutBtn')?.addEventListener('click', zoomOutHandler);
|
||||
document.getElementById('memoryZoomOutBtn')?.addEventListener('click', zoomOutHandler);
|
||||
document.getElementById('diskZoomOutBtn')?.addEventListener('click', zoomOutHandler);
|
||||
document.getElementById('networkZoomOutBtn')?.addEventListener('click', zoomOutHandler);
|
||||
document.getElementById('speedZoomOutBtn')?.addEventListener('click', zoomOutHandler);
|
||||
const cpuZoomOutBtn = document.getElementById('cpuZoomOutBtn');
|
||||
if (cpuZoomOutBtn) {
|
||||
cpuZoomOutBtn.addEventListener('click', zoomOutHandler);
|
||||
}
|
||||
const memoryZoomOutBtn = document.getElementById('memoryZoomOutBtn');
|
||||
if (memoryZoomOutBtn) {
|
||||
memoryZoomOutBtn.addEventListener('click', zoomOutHandler);
|
||||
}
|
||||
const diskZoomOutBtn = document.getElementById('diskZoomOutBtn');
|
||||
if (diskZoomOutBtn) {
|
||||
diskZoomOutBtn.addEventListener('click', zoomOutHandler);
|
||||
}
|
||||
const networkZoomOutBtn = document.getElementById('networkZoomOutBtn');
|
||||
if (networkZoomOutBtn) {
|
||||
networkZoomOutBtn.addEventListener('click', zoomOutHandler);
|
||||
}
|
||||
const speedZoomOutBtn = document.getElementById('speedZoomOutBtn');
|
||||
if (speedZoomOutBtn) {
|
||||
speedZoomOutBtn.addEventListener('click', zoomOutHandler);
|
||||
}
|
||||
|
||||
// 重置缩放处理函数
|
||||
const resetZoomHandler = () => {
|
||||
@@ -3033,11 +3074,26 @@ const resetZoomHandler = () => {
|
||||
};
|
||||
|
||||
// 为所有重置按钮添加事件监听器
|
||||
document.getElementById('cpuResetZoomBtn')?.addEventListener('click', resetZoomHandler);
|
||||
document.getElementById('memoryResetZoomBtn')?.addEventListener('click', resetZoomHandler);
|
||||
document.getElementById('diskResetZoomBtn')?.addEventListener('click', resetZoomHandler);
|
||||
document.getElementById('networkResetZoomBtn')?.addEventListener('click', resetZoomHandler);
|
||||
document.getElementById('speedResetZoomBtn')?.addEventListener('click', resetZoomHandler);
|
||||
const cpuResetZoomBtn = document.getElementById('cpuResetZoomBtn');
|
||||
if (cpuResetZoomBtn) {
|
||||
cpuResetZoomBtn.addEventListener('click', resetZoomHandler);
|
||||
}
|
||||
const memoryResetZoomBtn = document.getElementById('memoryResetZoomBtn');
|
||||
if (memoryResetZoomBtn) {
|
||||
memoryResetZoomBtn.addEventListener('click', resetZoomHandler);
|
||||
}
|
||||
const diskResetZoomBtn = document.getElementById('diskResetZoomBtn');
|
||||
if (diskResetZoomBtn) {
|
||||
diskResetZoomBtn.addEventListener('click', resetZoomHandler);
|
||||
}
|
||||
const networkResetZoomBtn = document.getElementById('networkResetZoomBtn');
|
||||
if (networkResetZoomBtn) {
|
||||
networkResetZoomBtn.addEventListener('click', resetZoomHandler);
|
||||
}
|
||||
const speedResetZoomBtn = document.getElementById('speedResetZoomBtn');
|
||||
if (speedResetZoomBtn) {
|
||||
speedResetZoomBtn.addEventListener('click', resetZoomHandler);
|
||||
}
|
||||
|
||||
|
||||
// 工具函数
|
||||
|
||||
Reference in New Issue
Block a user