Files
iDRAC_Info/backend/static/js/server_config.js
T
unknown 40150cd66d update
2026-03-21 07:26:38 +09:00

325 lines
11 KiB
JavaScript

let allServers = [];
let analysisData = {};
document.addEventListener('DOMContentLoaded', () => {
loadServers();
document.getElementById('check-all').addEventListener('change', (e) => {
const checked = e.target.checked;
document.querySelectorAll('.server-checkbox').forEach(cb => cb.checked = checked);
});
});
async function loadServers() {
try {
const res = await axios.get('/idrac/api/servers');
if (res.data.success) {
allServers = res.data.servers;
renderServerList(allServers);
} else {
alert('서버 목록 로드 실패: ' + res.data.message);
}
} catch (e) {
console.error(e);
alert('서버 목록 로드 중 오류가 발생했습니다.');
}
}
function renderServerList(servers) {
const tbody = document.querySelector('#selection-table tbody');
tbody.innerHTML = '';
if (servers.length === 0) {
tbody.innerHTML = '<tr><td colspan="5">등록된 서버가 없습니다.</td></tr>';
return;
}
servers.forEach(server => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td><input type="checkbox" class="server-checkbox" value="${server.id}"></td>
<td>${server.name}</td>
<td>${server.ip_address}</td>
<td>${server.model || '-'}</td>
<td>${server.status || 'Unknown'}</td>
`;
tbody.appendChild(tr);
});
}
// 시작: 서버 선택 후 동기화 (iDRAC -> DB)
async function syncSelectedServers() {
const selectedIds = Array.from(document.querySelectorAll('.server-checkbox:checked')).map(cb => parseInt(cb.value));
if (selectedIds.length === 0) {
alert('동기화할 서버를 하나 이상 선택해주세요.');
return;
}
if (!confirm(`선택한 ${selectedIds.length}대 서버의 설정을 iDRAC에서 가져와 DB에 저장하시겠습니까?`)) {
return;
}
document.getElementById('loading').style.display = 'flex';
updateLoadingMessage('설정 데이터 동기화 중...');
try {
const res = await axios.post('/server-config/api/sync', {
server_ids: selectedIds,
categories: ['bios', 'system', 'idrac']
});
if (res.data.success) {
const failed = res.data.results.failed;
if (failed.length > 0) {
const failedNames = failed.map(f => f.name || f.id).join(', ');
alert(`일부 서버 동기화 실패:\n${failedNames}\n\n성공한 서버는 DB에 저장되었습니다.`);
} else {
alert('모든 서버 동기화가 완료되었습니다. 이제 분석을 수행할 수 있습니다.');
}
// 만약 이미 결과 화면이 보이고 있다면 자동 갱신
if (document.getElementById('result-card').style.display !== 'none') {
startAnalysis();
}
} else {
alert('동기화 실패: ' + res.data.message);
}
} catch (e) {
console.error(e);
alert('동기화 요청 중 오류가 발생했습니다: ' + e.message);
} finally {
document.getElementById('loading').style.display = 'none';
}
}
// 시작: DB 데이터 기반 분석 (DB -> Web)
async function startAnalysis() {
const selectedIds = Array.from(document.querySelectorAll('.server-checkbox:checked')).map(cb => parseInt(cb.value));
if (selectedIds.length === 0) {
alert('분석할 서버를 하나 이상 선택해주세요.');
return;
}
document.getElementById('loading').style.display = 'flex';
document.getElementById('result-card').style.display = 'none';
updateLoadingMessage('데이터 분석 중...');
try {
const res = await axios.post('/server-config/api/fetch', {
server_ids: selectedIds,
categories: ['bios', 'system', 'idrac']
});
if (res.data.success) {
analysisData = res.data.results;
renderComparison();
document.getElementById('result-card').style.display = 'block';
} else {
alert('데이터 조회 실패: ' + res.data.message);
}
} catch (e) {
console.error(e);
alert('데이터 조회 중 오류가 발생했습니다.');
} finally {
document.getElementById('loading').style.display = 'none';
}
}
function updateLoadingMessage(msg) {
const loadingText = document.getElementById('loading-text');
if (loadingText) loadingText.textContent = msg;
}
function renderComparison() {
const container = document.getElementById('result-container');
const showDiffOnly = document.getElementById('show-diff-only').checked;
container.innerHTML = '';
const serverIds = Object.keys(analysisData);
if (serverIds.length === 0) {
container.innerHTML = '<p>데이터가 없습니다.</p>';
return;
}
// 통합된 모든 키(Attribute Name) 수집
const allKeys = new Set();
const serverNames = {};
const serverUpdated = {}; // 마지막 업데이트 시간
serverIds.forEach(id => {
const data = analysisData[id];
serverNames[id] = data.name;
// 가장 최근 업데이트 시간 찾기
const times = [data.bios_updated, data.idrac_updated, data.system_updated].filter(t => t);
if (times.length > 0) {
times.sort();
serverUpdated[id] = times[times.length - 1]; // 가장 최신
} else {
serverUpdated[id] = 'N/A';
}
// BIOS
if (data.bios) Object.keys(data.bios).forEach(k => allKeys.add('BIOS: ' + k));
// iDRAC
if (data.idrac) Object.keys(data.idrac).forEach(k => allKeys.add('iDRAC: ' + k));
// System
if (data.system) Object.keys(flattenObject(data.system)).forEach(k => allKeys.add('System: ' + k));
});
const sortedKeys = Array.from(allKeys).sort();
// 테이블 생성
const table = document.createElement('table');
table.className = 'comparison-table';
// 헤더
const thead = document.createElement('thead');
let headerHtml = '<tr><th style="min-width: 200px;">Attribute</th>';
serverIds.forEach(id => {
headerHtml += `<th>${serverNames[id]}</th>`;
});
headerHtml += '</tr>';
// 업데이트 시간 행 추가
headerHtml += '<tr style="background-color: #fafafa; font-size: 0.8em; color: #666;"><th>Local DB Updated</th>';
serverIds.forEach(id => {
const timeStr = serverUpdated[id] !== 'N/A' ? new Date(serverUpdated[id]).toLocaleString() : 'Never';
headerHtml += `<th>${timeStr}</th>`;
});
headerHtml += '</tr>';
thead.innerHTML = headerHtml;
table.appendChild(thead);
// 바디
const tbody = document.createElement('tbody');
let hasDiff = false;
sortedKeys.forEach(key => {
const row = document.createElement('tr');
let rowHtml = `<td>${key}</td>`;
// 값 비교를 위한 배열
const values = [];
serverIds.forEach(id => {
const val = getValueByKey(analysisData[id], key);
values.push(val);
});
// 값들이 모두 같은지 확인
const firstVal = values[0];
const isDiff = !values.every(v => v === firstVal);
if (isDiff) {
row.classList.add('diff-row');
hasDiff = true;
}
if (showDiffOnly && !isDiff) {
return; // Diff Only 모드이고 차이가 없으면 건너뜀
}
values.forEach(val => {
const displayVal = (val === undefined || val === null) ? '<span style="color:#ccc">N/A</span>' : val;
const cellClass = (isDiff && val !== firstVal) ? 'diff-highlight' : ''; // 첫번째 서버(기준)와 다르면 강조
rowHtml += `<td class="${cellClass}">${displayVal}</td>`;
});
row.innerHTML = rowHtml;
tbody.appendChild(row);
});
if (showDiffOnly && !hasDiff) {
// 차이점이 하나도 없는데 차이점만 보기 체크된 경우 빈 row가 아니라 메시지 표시가 나을지 고민
// tbody가 비어있음
}
if (tbody.children.length === 0) {
tbody.innerHTML = '<tr><td colspan="' + (serverIds.length + 1) + '">표시할 항목이 없습니다.</td></tr>';
}
table.appendChild(tbody);
container.appendChild(table);
}
// 헬퍼: 키(Category: Name)로 값 추출
function getValueByKey(data, compositeKey) {
if (!data) return undefined;
const [category, ...rest] = compositeKey.split(': ');
const realKey = rest.join(': '); // 혹시 키 안에 콜론이 있을 경우 복구
if (category === 'BIOS') return data.bios ? data.bios[realKey] : undefined;
if (category === 'iDRAC') return data.idrac ? data.idrac[realKey] : undefined;
if (category === 'System') {
const flatSys = flattenObject(data.system);
return flatSys ? flatSys[realKey] : undefined;
}
return undefined;
}
// 객체 평탄화 (Nested Object -> Flat Key)
function flattenObject(obj, prefix = '') {
if (!obj) return {};
return Object.keys(obj).reduce((acc, k) => {
const pre = prefix.length ? prefix + '.' : '';
if (typeof obj[k] === 'object' && obj[k] !== null) {
Object.assign(acc, flattenObject(obj[k], pre + k));
} else {
acc[pre + k] = obj[k];
}
return acc;
}, {});
}
function downloadReport() {
const table = document.querySelector('.comparison-table');
if (!table) {
alert('다운로드할 데이터가 없습니다. 먼저 분석을 진행해주세요.');
return;
}
let csvContent = "\uFEFF"; // BOM for Excel UTF-8
const rows = table.querySelectorAll('tr');
rows.forEach(row => {
const cols = row.querySelectorAll('th, td');
const rowData = [];
cols.forEach(col => {
// 따옴표 처리 및 텍스트 추출
let text = col.innerText || col.textContent;
text = text.replace(/"/g, '""'); // 따옴표 이스케이프
// 줄바꿈 제거
text = text.replace(/(\r\n|\n|\r)/gm, " ");
if (text.includes(',') || text.includes('"')) {
text = `"${text}"`;
}
rowData.push(text);
});
csvContent += rowData.join(',') + "\r\n";
});
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
const timestamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');
link.setAttribute('href', url);
link.setAttribute('download', `server_config_comparison_${timestamp}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}