You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
11 KiB
291 lines
11 KiB
// 判断内网还是公网
|
|
let apiURL = '';
|
|
fetch('/index.html') // 随便请求一个资源
|
|
.then(response => {
|
|
// 获取X-Environment响应头
|
|
const environment = response.headers.get('X-Environment');
|
|
|
|
// 根据环境变量设置API URL
|
|
if (environment === 'internal') {
|
|
apiURL = 'http://10.1.16.174:15001';
|
|
} else {
|
|
apiURL = 'http://gpus.lxblxb.top';
|
|
}
|
|
|
|
console.log('访问地址: ' + apiURL);
|
|
})
|
|
|
|
// 请求服务器获取数据
|
|
function fetchData() {
|
|
fetch(apiURL + '/api/get_data')
|
|
// 获取服务器和显卡数据
|
|
.then(response => response.json()) // 解析 JSON 响应
|
|
.then(data => {
|
|
// 处理 JSON 数据
|
|
// console.log(data);
|
|
displayServerData(data); // 调用显示数据的函数
|
|
})
|
|
.catch(error => {
|
|
// console.error('Error fetching data:', error);
|
|
displayError(error + " (多半是没有正确连接服务器端,可能是没开、网络错误)");
|
|
});
|
|
}
|
|
|
|
// 显示错误
|
|
function displayError(err_info){
|
|
let serverDataContainer = document.getElementById('server-data');
|
|
serverDataContainer.innerHTML = ''; // 清空容器
|
|
|
|
let errDiv = document.createElement('div');
|
|
errDiv.classList.add('error-info');
|
|
errDiv.innerText = err_info;
|
|
serverDataContainer.appendChild(errDiv);
|
|
}
|
|
|
|
// 将KB转为合适的格式
|
|
function parse_data_unit(num, fixedLen=2){
|
|
if (num < 1024){
|
|
return num.toFixed(fixedLen) + " KB";
|
|
}
|
|
|
|
num /= 1024;
|
|
if (num < 1024){
|
|
return num.toFixed(fixedLen) + " MB";
|
|
}
|
|
|
|
num /= 1024;
|
|
if (num < 1024){
|
|
return num.toFixed(fixedLen) + " GB";
|
|
}
|
|
|
|
num /= 1024;
|
|
if (num < 1024){
|
|
return num.toFixed(fixedLen) + " TB";
|
|
}
|
|
}
|
|
|
|
|
|
function add_bar(serverCard){
|
|
let bar = document.createElement('hr');
|
|
serverCard.appendChild(bar);
|
|
}
|
|
|
|
// 添加服务器信息的元素
|
|
function displayServerData(data){
|
|
let serverDataContainer = document.getElementById('server-data');
|
|
serverDataContainer.innerHTML = ''; // 清空容器
|
|
|
|
let serverDataDict = data['server_dict'];
|
|
|
|
// 创建每一个服务器的信息
|
|
for (let serverTitle in serverDataDict){
|
|
let serverCard = document.createElement('div');
|
|
serverCard.className = 'card';
|
|
|
|
// 标题
|
|
let serverName = document.createElement('div');
|
|
serverName.className = 'server-name';
|
|
serverName.textContent = serverTitle;
|
|
serverCard.appendChild(serverName);
|
|
|
|
serverData = serverDataDict[serverTitle];
|
|
// 如果没有数据则跳过
|
|
if (serverData == null){
|
|
let errText = document.createElement('div');
|
|
errText.className = 'error-text';
|
|
errText.textContent = "No data.";
|
|
serverCard.appendChild(errText);
|
|
serverDataContainer.appendChild(serverCard);
|
|
continue;
|
|
}
|
|
|
|
// 添加公告
|
|
if ('note' in serverData && serverData['note'] != ''){
|
|
let noteInfo = document.createElement('div');
|
|
noteInfo.className = 'note-info';
|
|
|
|
noteInfo.innerHTML = '<div style="text-align: center;"><strong>公告</strong></div>' + serverData['note'];
|
|
|
|
serverCard.appendChild(noteInfo);
|
|
}
|
|
|
|
// 判断时间
|
|
let lastTime = new Date(serverData['update_time_stamp'] * 1000);
|
|
let timeFromUpdate = Date.now() - lastTime;
|
|
if (timeFromUpdate > serverData['interval'] * 1000 * 4){
|
|
let errText = document.createElement('div');
|
|
errText.className = 'error-text';
|
|
errText.textContent = "长时间未更新,上次更新时间: " + lastTime.toLocaleString();
|
|
serverCard.appendChild(errText);
|
|
serverDataContainer.appendChild(serverCard);
|
|
continue;
|
|
}else if (timeFromUpdate > serverData['interval'] * 1000 * 2.5){
|
|
serverName.textContent = serverTitle + " - Not update -";
|
|
}
|
|
|
|
// 网速
|
|
if ('network_list' in serverData){
|
|
let networkInfo = document.createElement('div');
|
|
networkInfo.className = 'network-info';
|
|
|
|
// todo 暂时采用所有网卡总和的方法
|
|
let inSum = 0;
|
|
let outSum = 0;
|
|
let tmpTitle = "";
|
|
serverData.network_list.forEach(function(network){
|
|
inSum += network['in'];
|
|
outSum += network['out'];
|
|
tmpTitle += network['name'] + " in: " + parse_data_unit(network['in']) + "/s out: " + parse_data_unit(network['out']) + "/s\n";
|
|
});
|
|
|
|
let inStr = parse_data_unit(inSum);
|
|
let outStr = parse_data_unit(outSum);
|
|
|
|
networkInfo.innerHTML += "<strong> 网络 : </strong> <span title=\"" + tmpTitle + "\">in:" + inStr + "/s out:" + outStr + "/s</span><br>";
|
|
|
|
serverCard.appendChild(networkInfo);
|
|
// 分割线
|
|
add_bar(serverCard);
|
|
}
|
|
|
|
// CPU
|
|
if ('cpu' in serverData){
|
|
let cpuInfo = document.createElement('div');
|
|
cpuInfo.className = 'cpu-info';
|
|
|
|
temperature_list_str = "";
|
|
serverData.cpu['temperature_list'].forEach(function(v){
|
|
temperature_list_str += v + " ℃ ";
|
|
});
|
|
cpuInfo.innerHTML = "<strong>" + serverData.cpu['name'] + "</strong><br>" +
|
|
"<strong>温度 : </strong>" + temperature_list_str + "<br>" +
|
|
"<strong>占用率 : </strong><span title=\"" + serverData.cpu['core_occupy_list'] + "\">" + serverData.cpu['core_avg_occupy'] + "%";
|
|
|
|
serverCard.appendChild(cpuInfo);
|
|
// 分割线
|
|
add_bar(serverCard);
|
|
}
|
|
|
|
// 内存
|
|
if ('memory' in serverData){
|
|
let memoryInfo = document.createElement('div');
|
|
memoryInfo.className = 'memory-info';
|
|
let totalNum = serverData.memory.total
|
|
let usedNum = serverData.memory.used
|
|
let totalMem = parse_data_unit(totalNum);
|
|
let usedMem = parse_data_unit(usedNum);
|
|
let tmpClass = "state-free";
|
|
if (usedNum / totalNum > 0.95)
|
|
tmpClass = "state-super-occupy";
|
|
else if (usedNum / totalNum > 0.8)
|
|
tmpClass = "state-occupy";
|
|
else if (usedNum / totalNum > 0.6)
|
|
tmpClass = "state-light-occupy";
|
|
memoryInfo.innerHTML += "<strong> 内存 : </strong> <span class=\"" + tmpClass + "\">" + usedMem + " / " + totalMem + "</span><br>";
|
|
serverCard.appendChild(memoryInfo);
|
|
// 分割线
|
|
add_bar(serverCard);
|
|
}
|
|
|
|
// 存储空间
|
|
if ('storage_list' in serverData){
|
|
let storageInfo = document.createElement('div');
|
|
storageInfo.className = 'storage-info';
|
|
|
|
for (let i = 0; i < serverData.storage_list.length; i++) {
|
|
let targetPath = serverData.storage_list[i].path;
|
|
let totalNum = serverData.storage_list[i].total
|
|
let availableNum = serverData.storage_list[i].available
|
|
let totalStorage = parse_data_unit(totalNum);
|
|
let availableStorage = parse_data_unit(totalNum - availableNum);
|
|
let tmpClass = "state-free";
|
|
if (availableNum / totalNum < 0.01)
|
|
tmpClass = "state-super-occupy";
|
|
else if (availableNum / totalNum < 0.1)
|
|
tmpClass = "state-occupy";
|
|
else if (availableNum / totalNum < 0.3)
|
|
tmpClass = "state-light-occupy";
|
|
storageInfo.innerHTML += '<strong>' + targetPath + " :</strong> <span class=\"" + tmpClass
|
|
+ "\">" + availableStorage + " / " + totalStorage + "</span><br>";
|
|
}
|
|
|
|
serverCard.appendChild(storageInfo);
|
|
// 分割线
|
|
add_bar(serverCard);
|
|
}
|
|
|
|
// gpu
|
|
if ('gpu_list' in serverData){
|
|
serverData.gpu_list.forEach(function(gpu){
|
|
let gpuInfo = document.createElement('div');
|
|
gpuInfo.className = 'gpu-info';
|
|
|
|
let markFree = '<span class="state-free"> 空闲</span>';
|
|
let markLightOccupy = '<span class="state-light-occupy"> 占用</span>';
|
|
let markOccupy = '<span class="state-occupy"> 占用</span>';
|
|
let tmpMark = markFree;
|
|
let memory_used_ratio = gpu.used_memory / gpu.total_memory;
|
|
if (memory_used_ratio > 0.25 && gpu.utilization > 50){
|
|
tmpMark = markOccupy;
|
|
}
|
|
else if (memory_used_ratio > 0.25 || gpu.utilization > 50){
|
|
tmpMark = markLightOccupy;
|
|
}else{
|
|
tmpMark = markFree;
|
|
}
|
|
gpuInfo.innerHTML = '<strong>' + gpu.idx + ' - ' + gpu.name + tmpMark + '</strong><br>'
|
|
+ '温度: ' + gpu.temperature + '°C<br>'
|
|
+ '显存: ' + gpu.used_memory + ' / ' + gpu.total_memory + " MB" + '<br>'
|
|
+ '利用率: ' + gpu.utilization + '%';
|
|
|
|
// 添加进程信息
|
|
let processInfo = document.createElement('div');
|
|
processInfo.classList.add('process-info');
|
|
processInfo.innerHTML = "使用情况: ";
|
|
|
|
gpu.process_list.sort((a, b) => b.memory - a.memory);
|
|
gpu.process_list.forEach(function(item, index){
|
|
if (item.memory > 40)
|
|
processInfo.innerHTML += `<span class="process-item" title="${item.cmd}">${item.user} (${item.memory}) </span>`;
|
|
});
|
|
gpuInfo.appendChild(processInfo); // 将用户信息添加到GPU信息中
|
|
|
|
serverCard.appendChild(gpuInfo);
|
|
});
|
|
// 分割线
|
|
add_bar(serverCard);
|
|
}
|
|
|
|
// 错误信息
|
|
if ('error_dict' in serverData){
|
|
let errorInfo = document.createElement('div');
|
|
errorInfo.className = 'storage-info';
|
|
|
|
if (Object.keys(serverData.error_dict).length > 0){
|
|
for (let k in serverData.error_dict){
|
|
errorInfo.innerHTML += '<strong>' + k + " :</strong>" + serverData.error_dict[k] + "<br>";
|
|
}
|
|
|
|
serverCard.appendChild(errorInfo);
|
|
// 分割线
|
|
add_bar(serverCard);
|
|
}
|
|
}
|
|
|
|
// 删除最后的分割线
|
|
if (serverCard.lastElementChild && serverCard.lastElementChild.tagName === 'HR') {
|
|
serverCard.removeChild(serverCard.lastElementChild);
|
|
}
|
|
|
|
// 单个服务器信息作为child加入
|
|
serverDataContainer.appendChild(serverCard);
|
|
}
|
|
}
|
|
|
|
// TODO test
|
|
// fetchData()
|
|
// 页面加载时获取数据并定时刷新
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
fetchData();
|
|
setInterval(fetchData, 4000); // 每4秒刷新一次数据
|
|
});
|
|
|