v1.2.0: Enhanced Inspur/Kaytus parser with GPU, PCIe, and storage support
Major improvements: - Add CSV SEL event parser for Kaytus firmware format - Add PCIe device parser with link speed/width detection - Add GPU temperature and PCIe link monitoring - Add disk backplane parser for storage bay information - Fix memory module detection (only show installed DIMMs) Parser enhancements: - Parse RESTful PCIe Device info (max/current link width/speed) - Parse GPU sensor data (core and memory temperatures) - Parse diskbackplane info (slot count, installed drives) - Parse SEL events from CSV format (selelist.csv) - Fix memory Present status logic (check mem_mod_status) Web interface improvements: - Add PCIe link degradation highlighting (red when current < max) - Add storage table with Present status and location - Update memory specification to show only installed modules with frequency - Sort events from newest to oldest - Filter out N/A serial numbers from display Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -326,20 +326,24 @@ function renderConfig(data) {
|
||||
if (storNVMe > 0) typesSummary.push(`${storNVMe} NVMe`);
|
||||
html += `<h3>Накопители</h3>
|
||||
<div class="section-overview">
|
||||
<div class="stat-box"><span class="stat-value">${storTotal}</span><span class="stat-label">Всего</span></div>
|
||||
<div class="stat-box"><span class="stat-value">${totalTB} TB</span><span class="stat-label">Объём</span></div>
|
||||
<div class="stat-box"><span class="stat-value">${storTotal}</span><span class="stat-label">Всего слотов</span></div>
|
||||
<div class="stat-box"><span class="stat-value">${config.storage.filter(s => s.present).length}</span><span class="stat-label">Установлено</span></div>
|
||||
<div class="stat-box"><span class="stat-value">${totalTB > 0 ? totalTB + ' TB' : '-'}</span><span class="stat-label">Объём</span></div>
|
||||
<div class="stat-box model-box"><span class="stat-value">${typesSummary.join(', ') || '-'}</span><span class="stat-label">По типам</span></div>
|
||||
</div>
|
||||
<table class="config-table"><thead><tr><th>Слот</th><th>Тип</th><th>Интерфейс</th><th>Модель</th><th>Производитель</th><th>Размер</th><th>Серийный номер</th></tr></thead><tbody>`;
|
||||
<table class="config-table"><thead><tr><th>NO.</th><th>Статус</th><th>Расположение</th><th>Backplane ID</th><th>Тип</th><th>Модель</th><th>Размер</th><th>Серийный номер</th></tr></thead><tbody>`;
|
||||
config.storage.forEach(s => {
|
||||
const presentIcon = s.present ? '<span style="color: #27ae60;">●</span>' : '<span style="color: #95a5a6;">○</span>';
|
||||
const presentText = s.present ? 'Present' : 'Empty';
|
||||
html += `<tr>
|
||||
<td>${escapeHtml(s.slot || '-')}</td>
|
||||
<td>${presentIcon} ${presentText}</td>
|
||||
<td>${escapeHtml(s.location || '-')}</td>
|
||||
<td>${s.backplane_id !== undefined ? s.backplane_id : '-'}</td>
|
||||
<td>${escapeHtml(s.type || '-')}</td>
|
||||
<td>${escapeHtml(s.interface || '-')}</td>
|
||||
<td>${escapeHtml(s.model || '-')}</td>
|
||||
<td>${escapeHtml(s.manufacturer || '-')}</td>
|
||||
<td>${s.size_gb} GB</td>
|
||||
<td><code>${escapeHtml(s.serial_number || '-')}</code></td>
|
||||
<td>${s.size_gb > 0 ? s.size_gb + ' GB' : '-'}</td>
|
||||
<td>${s.serial_number ? '<code>' + escapeHtml(s.serial_number) + '</code>' : '-'}</td>
|
||||
</tr>`;
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
@@ -362,12 +366,18 @@ function renderConfig(data) {
|
||||
</div>
|
||||
<table class="config-table"><thead><tr><th>Слот</th><th>Модель</th><th>Производитель</th><th>BDF</th><th>PCIe</th><th>Серийный номер</th></tr></thead><tbody>`;
|
||||
config.gpus.forEach(gpu => {
|
||||
const pcieLink = formatPCIeLink(
|
||||
gpu.current_link_width || gpu.link_width,
|
||||
gpu.current_link_speed || gpu.link_speed,
|
||||
gpu.max_link_width,
|
||||
gpu.max_link_speed
|
||||
);
|
||||
html += `<tr>
|
||||
<td>${escapeHtml(gpu.slot || '-')}</td>
|
||||
<td>${escapeHtml(gpu.model || '-')}</td>
|
||||
<td>${escapeHtml(gpu.manufacturer || '-')}</td>
|
||||
<td><code>${escapeHtml(gpu.bdf || '-')}</code></td>
|
||||
<td>x${gpu.link_width || '-'} ${escapeHtml(gpu.link_speed || '-')}</td>
|
||||
<td>${pcieLink}</td>
|
||||
<td><code>${escapeHtml(gpu.serial_number || '-')}</code></td>
|
||||
</tr>`;
|
||||
});
|
||||
@@ -416,7 +426,12 @@ function renderConfig(data) {
|
||||
if (config.pcie_devices && config.pcie_devices.length > 0) {
|
||||
html += '<h3>PCIe устройства</h3><table class="config-table"><thead><tr><th>Слот</th><th>BDF</th><th>Тип</th><th>Производитель</th><th>Vendor:Device ID</th><th>PCIe Link</th></tr></thead><tbody>';
|
||||
config.pcie_devices.forEach(p => {
|
||||
const pcieLink = formatPCIeLink(p.link_width, p.link_speed);
|
||||
const pcieLink = formatPCIeLink(
|
||||
p.link_width,
|
||||
p.link_speed,
|
||||
p.max_link_width,
|
||||
p.max_link_speed
|
||||
);
|
||||
html += `<tr>
|
||||
<td>${escapeHtml(p.slot || '-')}</td>
|
||||
<td><code>${escapeHtml(p.bdf || '-')}</code></td>
|
||||
@@ -592,7 +607,8 @@ function renderSerials(serials) {
|
||||
};
|
||||
|
||||
serials.forEach(item => {
|
||||
if (!item.serial_number) return;
|
||||
// Skip items without serial number or with N/A
|
||||
if (!item.serial_number || item.serial_number === 'N/A') return;
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td><span class="category-badge ${item.category.toLowerCase()}">${categoryNames[item.category] || item.category}</span></td>
|
||||
@@ -711,23 +727,59 @@ function escapeHtml(text) {
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function formatPCIeLink(width, speed) {
|
||||
if (!width && !speed) return '-';
|
||||
|
||||
// Convert GT/s to PCIe generation
|
||||
let gen = '';
|
||||
if (speed) {
|
||||
function formatPCIeLink(currentWidth, currentSpeed, maxWidth, maxSpeed) {
|
||||
// Helper to convert speed to generation
|
||||
function speedToGen(speed) {
|
||||
if (!speed) return '';
|
||||
const gtMatch = speed.match(/(\d+\.?\d*)\s*GT/i);
|
||||
if (gtMatch) {
|
||||
const gts = parseFloat(gtMatch[1]);
|
||||
if (gts >= 32) gen = 'Gen5';
|
||||
else if (gts >= 16) gen = 'Gen4';
|
||||
else if (gts >= 8) gen = 'Gen3';
|
||||
else if (gts >= 5) gen = 'Gen2';
|
||||
else if (gts >= 2.5) gen = 'Gen1';
|
||||
if (gts >= 32) return 'Gen5';
|
||||
if (gts >= 16) return 'Gen4';
|
||||
if (gts >= 8) return 'Gen3';
|
||||
if (gts >= 5) return 'Gen2';
|
||||
if (gts >= 2.5) return 'Gen1';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
const widthStr = width ? `x${width}` : '';
|
||||
return gen ? `${widthStr} PCIe ${gen}` : `${widthStr} ${speed || ''}`;
|
||||
// Helper to extract GT/s value for comparison
|
||||
function extractGTs(speed) {
|
||||
if (!speed) return 0;
|
||||
const gtMatch = speed.match(/(\d+\.?\d*)\s*GT/i);
|
||||
return gtMatch ? parseFloat(gtMatch[1]) : 0;
|
||||
}
|
||||
|
||||
// If no data, return dash
|
||||
if (!currentWidth && !currentSpeed) return '-';
|
||||
|
||||
const curGen = speedToGen(currentSpeed);
|
||||
const maxGen = speedToGen(maxSpeed);
|
||||
|
||||
// Check if current is lower than max
|
||||
const widthDegraded = maxWidth && currentWidth && currentWidth < maxWidth;
|
||||
const speedDegraded = maxSpeed && currentSpeed && extractGTs(currentSpeed) < extractGTs(maxSpeed);
|
||||
|
||||
// Build current link string
|
||||
const curWidthStr = currentWidth ? `x${currentWidth}` : '';
|
||||
const curLinkStr = curGen ? `${curWidthStr} ${curGen}` : `${curWidthStr} ${currentSpeed || ''}`;
|
||||
|
||||
// Build max link string (if available)
|
||||
let maxLinkStr = '';
|
||||
if (maxWidth || maxSpeed) {
|
||||
const maxWidthStr = maxWidth ? `x${maxWidth}` : '';
|
||||
maxLinkStr = maxGen ? `${maxWidthStr} ${maxGen}` : `${maxWidthStr} ${maxSpeed || ''}`;
|
||||
}
|
||||
|
||||
// Apply degraded class if needed
|
||||
const degradedClass = (widthDegraded || speedDegraded) ? ' class="pcie-degraded"' : '';
|
||||
|
||||
// Format output: show "current" or "current / max" if max differs
|
||||
if (maxLinkStr && (widthDegraded || speedDegraded)) {
|
||||
return `<span${degradedClass}>${curLinkStr}</span> <span class="pcie-max">/ ${maxLinkStr}</span>`;
|
||||
} else if (maxLinkStr && maxLinkStr !== curLinkStr) {
|
||||
return `${curLinkStr} <span class="pcie-max">/ ${maxLinkStr}</span>`;
|
||||
} else {
|
||||
return curLinkStr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user