/**
* OpenManager AI - ๋๋ฏธ ๋ฐ์ดํฐ ์์ฑ๊ธฐ
* ์ค์๊ฐ ์๋ฒ ๋ชจ๋ํฐ๋ง์ ์ํ ํ์ค์ ์ธ ๋๋ฏธ ๋ฐ์ดํฐ๋ฅผ ์์ฑํฉ๋๋ค.
*
* ์ฃผ์ ๊ธฐ๋ฅ:
* - 50๋ ์๋ฒ์ ๋ํ ํ์ค์ ์ธ ๋ชจ๋ํฐ๋ง ๋ฐ์ดํฐ ์์ฑ
* - 10๋ถ๋ง๋ค ๋ฐ์ดํฐ ๊ฐฑ์ ๋ฐ ๋์ ์ ์ฅ (24์๊ฐ ์ด๋ ฅ ๋ณด๊ด)
* - ์๋ฒ ์ ํ๋ณ ํน์ฑ์ ๋ฐ๋ฅธ ๋ฐ์ดํฐ ์์ฑ (์น์๋ฒ, DB์๋ฒ, API์๋ฒ ๋ฑ)
* - ์ฝ 14%์ ์๋ฒ๊ฐ ๊ฒฝ๊ณ ๋๋ ์ฌ๊ฐ ์ํ๋ก ์์ฑ (์ฌ๊ฐ: 4%, ๊ฒฝ๊ณ : 10%)
*/
class DummyDataGenerator {
constructor() {
this.serverCount = 50; // 50๋ ์๋ฒ
this.initialBatchSize = 10; // ์ฒซ ๋ก๋ฉ์ 10๋๋ง ์ฐ์ ์์ฑ
this.updateInterval = 10 * 60 * 1000; // 10๋ถ (๋ฐ๋ฆฌ์ด ๋จ์)
// ์ต์ข
์ํ ๋ชฉํ์น (ai_processor.js๊ฐ ํ๋จ)
this.targetCriticalRatio = 0.03; // ์ฌ๊ฐ ์ํ ์๋ฒ ๋น์จ ๋ชฉํ (50๋ ์ค 1-2๋๋ก ์ค์)
this.targetWarningRatio = 0.06; // ๊ฒฝ๊ณ ์ํ ์๋ฒ ๋น์จ ๋ชฉํ (50๋ ์ค 3๋๋ก ์ค์)
// "์ฌ๋ฃ" ๋ฐ์ ํ๋ฅ (์ด ๊ฐ๋ค์ ์กฐ์ ํ์ฌ ์ ๋ชฉํ์น์ ๊ทผ์ ํ๋๋ก ํจ)
// ์ด ํ๋ฅ ๋ค์ ๋
๋ฆฝ์ ์ผ๋ก ์์ฉํ๊ฑฐ๋ ์ฌ๋ฌ๊ฐ๊ฐ ๋์์ ๋ฐ์ํ ์ ์์
this.highResourceUsageCriticalProb = 0.02; // ๋ฆฌ์์ค ํ๋๊ฐ 90% ๋์ ํ๋ฅ (์ค์)
this.highResourceUsageWarningProb = 0.04; // ๋ฆฌ์์ค ํ๋๊ฐ 70-89% ๋ ํ๋ฅ (์ค์)
this.criticalErrorProb = 0.01; // "Critical" ์ค๋ฅ ๋ฉ์์ง ๋ฐ์ ํ๋ฅ (์ค์)
this.warningErrorProb = 0.03; // "Error" ๋๋ "Warning" ์ค๋ฅ ๋ฉ์์ง ๋ฐ์ ํ๋ฅ (์ค์)
this.serviceStoppedProb = 0.01; // ์๋น์ค ์ค๋จ ๋ฐ์ ํ๋ฅ (์ค์)
// ์๋ฒ ๊ตฌ์ฑ ์ ๋ณด
this.serverConfigurations = [
// ์น ์๋ฒ (15๋)
{
prefix: 'web',
count: 15,
cpu: { base: 40, variation: 15 }, // ๊ธฐ๋ณธ CPU ์ฌ์ฉ๋ฅ 40% ยฑ 15%
memory: { base: 50, variation: 15 }, // ๊ธฐ๋ณธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ 50% ยฑ 15%
disk: { base: 45, variation: 10 }, // ๊ธฐ๋ณธ ๋์คํฌ ์ฌ์ฉ๋ฅ 45% ยฑ 10%
services: ['nginx', 'php-fpm', 'varnish', 'haproxy'],
serviceCount: { min: 2, max: 4 }
},
// ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ (10๋)
{
prefix: 'app',
count: 10,
cpu: { base: 55, variation: 20 },
memory: { base: 60, variation: 15 },
disk: { base: 40, variation: 10 },
services: ['tomcat', 'nodejs', 'pm2', 'supervisord', 'docker'],
serviceCount: { min: 2, max: 5 }
},
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ (8๋)
{
prefix: 'db',
count: 8,
cpu: { base: 45, variation: 15 },
memory: { base: 70, variation: 15 },
disk: { base: 65, variation: 15 },
services: ['mysql', 'postgresql', 'mongodb', 'redis', 'elasticsearch'],
serviceCount: { min: 1, max: 3 }
},
// ์บ์ ์๋ฒ (5๋)
{
prefix: 'cache',
count: 5,
cpu: { base: 30, variation: 20 },
memory: { base: 80, variation: 15 },
disk: { base: 25, variation: 10 },
services: ['redis', 'memcached', 'varnish'],
serviceCount: { min: 1, max: 2 }
},
// API ์๋ฒ (7๋)
{
prefix: 'api',
count: 7,
cpu: { base: 50, variation: 20 },
memory: { base: 55, variation: 15 },
disk: { base: 35, variation: 10 },
services: ['nginx', 'nodejs', 'python', 'gunicorn', 'uwsgi'],
serviceCount: { min: 2, max: 4 }
},
// ๋ชจ๋ํฐ๋ง ์๋ฒ (5๋)
{
prefix: 'monitor',
count: 5,
cpu: { base: 35, variation: 10 },
memory: { base: 50, variation: 10 },
disk: { base: 60, variation: 15 },
services: ['prometheus', 'grafana', 'influxdb', 'telegraf', 'alertmanager'],
serviceCount: { min: 2, max: 5 }
}
];
this.regions = ['kr', 'us', 'eu', 'jp', 'sg'];
this.osTypes = [
'Ubuntu 20.04 LTS',
'Ubuntu 22.04 LTS',
'CentOS 7',
'Rocky Linux 8',
'Amazon Linux 2',
'Debian 11',
'RHEL 8'
];
// ์๋ฒ ์๊ฐ ํจํด (์ผ๊ณผ ์๊ฐ์ ๋ถํ ์ฆ๊ฐ)
this.timePatterns = {
dailyPattern: [
// 0-23์๊ฐ, ๊ฐ์ค์น(0-100)
30, 20, 15, 10, 10, 15, // 0-5์ (์๋ฒฝ): ๋ฎ์ ๋ถํ
25, 40, 60, 70, 75, 80, // 6-11์ (์ค์ ): ์ ์ฐจ ์ฆ๊ฐ
85, 90, 85, 80, 75, 70, // 12-17์ (์คํ): ํผํฌ ํ ๊ฐ์
65, 60, 55, 50, 40, 35 // 18-23์ (์ ๋
): ์ ์ฐจ ๊ฐ์
]
};
// ์์ฑ ์ํ
this.isGenerating = false;
this.generatedCount = 0;
// ์ด๋ ฅ ๋ฐ์ดํฐ ์ ์ฅ (์๋ฒ๋ณ๋ก ๋ณด๊ด)
this.historicalData = {}; // ์๋ฒ๋ช
: [๋ฐ์ดํฐ ํฌ์ธํธ๋ค]
this.maxHistoricalPoints = 144; // 24์๊ฐ(10๋ถ ๊ฐ๊ฒฉ = 6๊ฐ/์๊ฐ * 24์๊ฐ)
// ํ๋ฃจ์น ๋ฐ์ดํฐ ์์ฑ ์๊ฐ ๋จ์
this.dayHours = 24;
this.currentHour = new Date().getHours(); // ํ์ฌ ์๊ฐ์ผ๋ก ์ด๊ธฐํ
// ์๋ฒ ๋ฐ์ดํฐ ์ด๊ธฐํ - ์ฒซ ๋ฐฐ์น๋ง ์ฆ์ ์์ฑ
this.serverData = [];
this.generateInitialBatch();
// ๋ฐ์ดํฐ ์๋ ์
๋ฐ์ดํธ ์์
this.startDataUpdates();
}
// ์ด๊ธฐ ์๋ฒ ๋ฐฐ์น ๋ฐ์ดํฐ ์์ฑ (๋น ๋ฅธ ๋ก๋ฉ์ ์ํด ์ผ๋ถ๋ง ์์ฑ)
generateInitialBatch() {
console.log(`์ด๊ธฐ ์๋ฒ ๋ฐ์ดํฐ ${this.initialBatchSize}๊ฐ ์์ฑ ์ค...`);
// ์๋ฒ ๊ตฌ์ฑ์ ๋ง๊ฒ ๋ฐฐํฌ
this.serverData = [];
let serverIndex = 0;
// ๋จผ์ ์ด๊ธฐ ๋ฐฐ์น ํฌ๊ธฐ๋งํผ๋ง ์์ฑ
for (let i = 0; i < this.initialBatchSize && serverIndex < this.serverCount; i++) {
const server = this.createServerByConfiguration(serverIndex);
this.serverData.push(server);
// ์ด๋ ฅ ๋ฐ์ดํฐ ์ด๊ธฐํ
if (!this.historicalData[server.hostname]) {
this.historicalData[server.hostname] = [];
}
this.historicalData[server.hostname].push({...server, timestamp: new Date().toISOString()});
serverIndex++;
}
// ์ ์ญ ๊ฐ์ฒด์ ๋ฐ์ดํฐ ํ ๋น
window.serverData = this.serverData;
// ์ด๋ฒคํธ ๋ฐ์
this.dispatchUpdateEvent();
// ๋๋จธ์ง ๋ฐ์ดํฐ ๋น๋๊ธฐ์ ์ผ๋ก ์์ฑ
this.generateRemainingServersAsync();
}
// ์๋ฒ ๊ตฌ์ฑ์ ๋ง๊ฒ ์๋ฒ ์์ฑ
createServerByConfiguration(index) {
// ์ด ๊ฐ์๋ฅผ ํ์ธํด์ ์ด๋ค ์ ํ์ ์๋ฒ๋ฅผ ์์ฑํ ์ง ๊ฒฐ์
let currentCount = 0;
let selectedConfig = null;
for (const config of this.serverConfigurations) {
if (index < currentCount + config.count) {
selectedConfig = config;
break;
}
currentCount += config.count;
}
// ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ (๋ง์ฝ ๋ชจ๋ ์ค์ ์ ๋ง์ง ์์ ๊ฒฝ์ฐ)
if (!selectedConfig) {
selectedConfig = this.serverConfigurations[0];
}
// ์ ํ๋ ๊ตฌ์ฑ์ผ๋ก ์๋ฒ ์์ฑ
const serverNumber = (index + 1).toString().padStart(3, '0');
const region = this.getRandomItem(this.regions);
const hostname = `${selectedConfig.prefix}-${region}-${serverNumber}`;
// ์๊ฐ๋๋ณ ๋ถํ ๊ฐ์ค์น ์ ์ฉ (ํ์ฌ ์๊ฐ ๊ธฐ์ค)
const timeWeightMultiplier = this.timePatterns.dailyPattern[this.currentHour] / 100;
// 1. ๋ฆฌ์์ค ์ฌ์ฉ๋ ์์ฑ
let cpuBase = selectedConfig.cpu.base + this.getRandomInt(-selectedConfig.cpu.variation, selectedConfig.cpu.variation);
let memoryBase = selectedConfig.memory.base + this.getRandomInt(-selectedConfig.memory.variation, selectedConfig.memory.variation);
let diskBase = selectedConfig.disk.base + this.getRandomInt(-selectedConfig.disk.variation, selectedConfig.disk.variation);
// ํ๋ฅ ์ ๋ฐ๋ผ ๋ฆฌ์์ค ์ฌ์ฉ๋ ๊ธ์ฆ ์๋ฎฌ๋ ์ด์
if (Math.random() < this.highResourceUsageCriticalProb) {
const resourceType = this.getRandomInt(1, 3); // 1:CPU, 2:Mem, 3:Disk
if (resourceType === 1) cpuBase = 90 + this.getRandomInt(0, 8);
else if (resourceType === 2) memoryBase = 90 + this.getRandomInt(0, 8);
else diskBase = 90 + this.getRandomInt(0, 8);
} else if (Math.random() < this.highResourceUsageWarningProb) {
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 1) cpuBase = 70 + this.getRandomInt(0, 19);
else if (resourceType === 2) memoryBase = 70 + this.getRandomInt(0, 19);
else diskBase = 70 + this.getRandomInt(0, 19);
}
const cpu_usage = parseFloat(Math.min(Math.max(Math.floor(cpuBase * timeWeightMultiplier) + this.getRandomInt(-10, 10), 5), 98).toFixed(2));
const memory_usage_percent = parseFloat(Math.min(Math.max(Math.floor(memoryBase * timeWeightMultiplier) + this.getRandomInt(-10, 10), 5), 98).toFixed(2));
const disk_info = [{
mount: '/data',
total: '100GB', // ์์ ๊ฐ
used: '0GB', // ์๋์์ ๊ณ์ฐ
available: '0GB', // ์๋์์ ๊ณ์ฐ
disk_usage_percent: parseFloat(Math.min(Math.max(Math.floor(diskBase * timeWeightMultiplier) + this.getRandomInt(-5, 5), 5), 98).toFixed(2))
}];
disk_info[0].used = (disk_info[0].disk_usage_percent / 100 * 100).toFixed(1) + 'GB';
disk_info[0].available = (100 - (disk_info[0].disk_usage_percent / 100 * 100)).toFixed(1) + 'GB';
// 2. ์ค๋ฅ ๋ฉ์์ง ์์ฑ
const errors = [];
if (Math.random() < this.criticalErrorProb) {
errors.push(this.generateErrorMessage(selectedConfig.prefix, 'Critical'));
} else if (Math.random() < this.warningErrorProb) {
const errorType = Math.random() < 0.5 ? 'Error' : 'Warning';
errors.push(this.generateErrorMessage(selectedConfig.prefix, errorType));
}
if (selectedConfig.prefix === 'db' && Math.random() < 0.1) { // DB ์๋ฒ๋ ๊ฐ๋ ์ถ๊ฐ ์ค๋ฅ
errors.push(this.generateErrorMessage('db', 'Warning', 'Slow query detected'));
}
// 3. ์๋น์ค ์ํ ์์ฑ
const services = {};
const numServices = this.getRandomInt(selectedConfig.serviceCount.min, selectedConfig.serviceCount.max);
const availableServices = [...selectedConfig.services];
let stoppedServiceInjected = false;
for (let i = 0; i < numServices && availableServices.length > 0; i++) {
const serviceIndex = this.getRandomInt(0, availableServices.length - 1);
const serviceName = availableServices.splice(serviceIndex, 1)[0];
let status = 'running';
// ์ฒซ ๋ฒ์งธ ์๋น์ค์ ๋ํด์๋ง ๋ฎ์ ํ๋ฅ ๋ก stopped ์ํ ์ฃผ์
(๋จ, criticalError๊ฐ ์ด๋ฏธ ๋ฐ์ํ์ง ์์๋ค๋ฉด)
// ์ฌ๋ฌ ์๋น์ค๊ฐ ๋์์ ์ค๋จ๋๋ ์ํฉ์ ai_processor์์ critical๋ก ์ฒ๋ฆฌ
if (i === 0 && !stoppedServiceInjected && errors.every(e => !e.toLowerCase().includes('critical')) && Math.random() < this.serviceStoppedProb) {
status = 'stopped';
stoppedServiceInjected = true; // ์ค๋ณต ๋ฐฉ์ง
}
services[serviceName] = status;
}
// 4. ๋คํธ์ํฌ ํธ๋ํฝ ๋ฐ ๊ธฐํ ์ ๋ณด
const net_rx_bytes = this.getRandomInt(100000, 50000000); // ์์ ๋ฒ์
const net_tx_bytes = this.getRandomInt(100000, 50000000); // ์์ ๋ฒ์
const server = {
hostname: hostname,
ip: `10.${index % 25}.${Math.floor(index / 25)}.${(index % 50) + 10}`,
os: this.getRandomItem(this.osTypes),
cpu_usage: cpu_usage,
memory_total: '16GB', // ์์
memory_usage_percent: memory_usage_percent,
memory_free: `${(100 - memory_usage_percent) / 100 * 16}GB`, // ์์
disk: disk_info,
services: services,
errors: errors,
net: {
rx_bytes: net_rx_bytes,
tx_bytes: net_tx_bytes,
rx_packets: this.getRandomInt(net_rx_bytes / 1000, net_rx_bytes / 500),
tx_packets: this.getRandomInt(net_tx_bytes / 1000, net_tx_bytes / 500),
rx_errors: errors.length > 0 && Math.random() < 0.2 ? this.getRandomInt(1, 50) : 0, // ์ค๋ฅ ์์ ์ ๋คํธ์ํฌ ์ค๋ฅ ํ๋ฅ ์ฆ๊ฐ
tx_errors: errors.length > 0 && Math.random() < 0.1 ? this.getRandomInt(1, 20) : 0
},
uptime: `${this.getRandomInt(1, 365)}d ${this.getRandomInt(0,23)}h ${this.getRandomInt(0,59)}m`,
load_avg: [parseFloat(Math.random().toFixed(2)), parseFloat(Math.random().toFixed(2)), parseFloat(Math.random().toFixed(2))],
processes: this.getRandomInt(50, 300),
last_updated: new Date().toISOString(),
region: region,
server_type: selectedConfig.prefix, // server_type ์ถ๊ฐ
// status ํ๋๋ ai_processor์์ ๊ฒฐ์ ํ๋ฏ๋ก ์ฌ๊ธฐ์๋ ์์ฑํ์ง ์์
};
return server;
}
// ๋๋จธ์ง ์๋ฒ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์์ฑ
generateRemainingServersAsync() {
if (this.isGenerating) return;
this.isGenerating = true;
this.generatedCount = this.initialBatchSize;
const batchSize = 5; // ํ ๋ฒ์ 5๊ฐ์ฉ ์์ฑ
const generateBatch = () => {
const remainingCount = this.serverCount - this.generatedCount;
if (remainingCount <= 0) {
console.log(`๋ชจ๋ ์๋ฒ ๋ฐ์ดํฐ ${this.serverCount}๊ฐ ์์ฑ ์๋ฃ`);
this.isGenerating = false;
return;
}
const batchCount = Math.min(batchSize, remainingCount);
console.log(`์๋ฒ ๋ฐ์ดํฐ ์์ฑ ์ค... (${this.generatedCount}/${this.serverCount})`);
// ๋ฐฐ์น ์์ฑ
const newServers = [];
for (let i = 0; i < batchCount; i++) {
const server = this.createServerByConfiguration(this.generatedCount + i);
newServers.push(server);
// ์ด๋ ฅ ๋ฐ์ดํฐ ์ด๊ธฐํ
if (!this.historicalData[server.hostname]) {
this.historicalData[server.hostname] = [];
}
this.historicalData[server.hostname].push({...server, timestamp: new Date().toISOString()});
}
// ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ถ๊ฐ
this.serverData = [...this.serverData, ...newServers];
this.generatedCount += batchCount;
// ์ ์ญ ๊ฐ์ฒด ์
๋ฐ์ดํธ
window.serverData = this.serverData;
// ์ด๋ฒคํธ ๋ฐ์
this.dispatchUpdateEvent();
// ๋ค์ ๋ฐฐ์น ์์ฝ (์ฝ๊ฐ์ ๋๋ ์ด๋ฅผ ๋์ด UI ์ฐจ๋จ ๋ฐฉ์ง)
setTimeout(generateBatch, 50);
};
// ์ฒซ ๋ฐฐ์น ์์ฑ ์์
setTimeout(generateBatch, 200);
}
// ๋ฐ์ดํฐ ์
๋ฐ์ดํธ ํจ์
updateData() {
// ํ๋ฃจ ์ฃผ๊ธฐ ์
๋ฐ์ดํธ
this.currentHour = (this.currentHour + 1) % this.dayHours;
// ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ์์ฑ๋์ง ์์์ผ๋ฉด ์
๋ฐ์ดํธ ์คํต
if (this.isGenerating) {
console.log("์์ง ๋ชจ๋ ์๋ฒ ๋ฐ์ดํฐ๊ฐ ์์ฑ๋์ง ์์์ต๋๋ค. ์
๋ฐ์ดํธ ์คํต");
return;
}
console.log(`์๋ฒ ๋ฐ์ดํฐ ์
๋ฐ์ดํธ ์ค... (${this.serverData.length}๊ฐ)`);
// ํ์ฌ ์๊ฐ๋์ ๊ฐ์ค์น ๊ณ์ฐ
const timeWeightMultiplier = this.timePatterns.dailyPattern[this.currentHour] / 100;
// ์ ํ์์คํฌํ ์์ฑ
const now = new Date();
now.setHours(this.currentHour, 0, 0, 0);
// ์ ์ฒด ์๋ฒ์์ ์ฌ๊ฐ ์ํ์ ๊ฒฝ๊ณ ์ํ์ ๊ฐ์๋ฅผ ์ถ์
let criticalCount = 0;
let warningCount = 0;
this.serverData = this.serverData.map((server, index) => {
const serverType = server.hostname.split('-')[0];
const config = this.serverConfigurations.find(c => c.prefix === serverType) || this.serverConfigurations[0];
// ์๋น์ค์ ์ค๋ฅ๋ 30% ํ๋ฅ ๋ก ์ฌ์์ฑ
const shouldUpdateServices = Math.random() < 0.3;
const shouldUpdateErrors = Math.random() < 0.3;
// ์๋ฒ ์ํ ๊ฒฐ์ (๊ธฐ์กด ์ํ๋ฅผ ๊ณ ๋ คํ์ฌ ๊ธ๊ฒฉํ ๋ณํ ๋ฐฉ์ง)
const isCritical = server.cpu_usage >= 90 || server.memory_usage_percent >= 90 || server.disk[0].disk_usage_percent >= 90;
const isWarning = !isCritical && (server.cpu_usage >= 70 || server.memory_usage_percent >= 70 || server.disk[0].disk_usage_percent >= 70);
// ์ฌ๊ฐ/๊ฒฝ๊ณ ์ํ ์๋ฒ ์ ์ถ์
if (isCritical) criticalCount++;
else if (isWarning) warningCount++;
// ์ํ ๋ณํ ํ๋ฅ ๊ณ์ฐ (ํ์ฌ ์ํ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ค์ )
let changeToCritical = false;
let changeToWarning = false;
let changeToNormal = false;
// ์ํ ์ ์ด ํ๋ฅ ๊ณ์ฐ
if (isCritical) {
// ์ฌ๊ฐ โ ์ ์ ๋๋ ๊ฒฝ๊ณ (20% ํ๋ฅ ๋ก ์ํ ๋ณ๊ฒฝ)
if (Math.random() < 0.2) {
changeToNormal = Math.random() < 0.5;
changeToWarning = !changeToNormal;
}
}
else if (isWarning) {
// ๊ฒฝ๊ณ โ ์ ์ ๋๋ ์ฌ๊ฐ (30% ํ๋ฅ ๋ก ์ํ ๋ณ๊ฒฝ)
if (Math.random() < 0.3) {
changeToNormal = Math.random() < 0.7; // 70% ํ๋ฅ ๋ก ์ ์์ผ๋ก ๋ณต๊ตฌ
changeToCritical = !changeToNormal; // 30% ํ๋ฅ ๋ก ์ฌ๊ฐ์ผ๋ก ์
ํ
}
}
else {
// ์ ์ โ ๊ฒฝ๊ณ ๋๋ ์ฌ๊ฐ
// ์ ์ฒด ์ฌ๊ฐ/๊ฒฝ๊ณ ์ํ ์๋ฒ ์์ ๋ฐ๋ผ ํ๋ฅ ์กฐ์
const targetCritical = Math.floor(this.serverData.length * this.targetCriticalRatio);
const targetWarning = Math.floor(this.serverData.length * this.targetWarningRatio);
// ์ฌ๊ฐ ์ํ๊ฐ ๋ชฉํ๋ณด๋ค ์ ์ผ๋ฉด ์ฌ๊ฐ ์ํ๋ก ๋ณ๊ฒฝ ํ๋ฅ ์ฆ๊ฐ
if (criticalCount < targetCritical && Math.random() < 0.05) {
changeToCritical = true;
}
// ๊ฒฝ๊ณ ์ํ๊ฐ ๋ชฉํ๋ณด๋ค ์ ์ผ๋ฉด ๊ฒฝ๊ณ ์ํ๋ก ๋ณ๊ฒฝ ํ๋ฅ ์ฆ๊ฐ
else if (warningCount < targetWarning && Math.random() < 0.1) {
changeToWarning = true;
}
}
// ํ์ฌ ์ํ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์ค์ ์
๋ฐ์ดํธ ์ํ
const cpu_base = config.cpu.base;
const cpu_variation = config.cpu.variation;
// ๊ธฐ๋ณธ ๋ณ๋๊ฐ ๊ณ์ฐ
let baseChange = this.getRandomInt(-15, 15);
// ์๊ฐ ํจํด์ ๊ณ ๋ คํ ๋ณ๋
const timeInfluence = cpu_base * (timeWeightMultiplier - 0.5) * 0.8; // -40% ~ +40% ๋ณ๋ ๊ฐ๋ฅ
// ์ํ ๋ณํ์ ๋ฐ๋ฅธ ๋ฆฌ์์ค ์ฌ์ฉ๋ ์กฐ์
if (changeToCritical) {
// ๋ฆฌ์์ค ์ค ๋๋คํ๊ฒ ํ๋๋ฅผ ์ฌ๊ฐ ์ํ๋ก ์ค์
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 1) {
baseChange = Math.max(90 - server.cpu_usage, baseChange); // CPU๋ฅผ 90% ์ด์์ผ๋ก
}
}
else if (changeToWarning) {
// ๋ฆฌ์์ค ์ค ๋๋คํ๊ฒ ํ๋๋ฅผ ๊ฒฝ๊ณ ์ํ๋ก ์ค์
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 1) {
baseChange = Math.max(70 - server.cpu_usage, baseChange); // CPU๋ฅผ 70% ์ด์์ผ๋ก
}
}
else if (changeToNormal) {
// ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ์ ์ ๋ฒ์๋ก ์กฐ์
if (server.cpu_usage >= 70) {
baseChange = -this.getRandomInt(10, 30); // CPU ์ฌ์ฉ๋ ํฌ๊ฒ ๊ฐ์
}
}
// ์ต์ข
CPU ๋ณ๋๊ฐ ๊ณ์ฐ
let cpu_delta = parseFloat((baseChange + timeInfluence).toFixed(2));
cpu_delta = Math.max(Math.min(cpu_delta, 20), -20); // ๋ณ๋ํญ ์ ํ
// ์ CPU ์ฌ์ฉ๋ ๊ณ์ฐ
let cpu_usage = parseFloat((server.cpu_usage + cpu_delta).toFixed(2));
cpu_usage = Math.max(5, Math.min(98, cpu_usage)); // 5% ~ 98% ์ฌ์ด๋ก ์ ํ
// ๋ค๋ฅธ ๋ฆฌ์์ค๋ ๋น์ทํ ํจํด์ผ๋ก ์
๋ฐ์ดํธ
let memory_delta = parseFloat((this.getRandomInt(-10, 10) + timeInfluence * 0.8).toFixed(2));
// ์ํ ๋ณํ์ ๋ฐ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์กฐ์
if (changeToCritical) {
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 2) {
memory_delta = Math.max(90 - server.memory_usage_percent, memory_delta); // ๋ฉ๋ชจ๋ฆฌ๋ฅผ 90% ์ด์์ผ๋ก
}
}
else if (changeToWarning) {
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 2) {
memory_delta = Math.max(70 - server.memory_usage_percent, memory_delta); // ๋ฉ๋ชจ๋ฆฌ๋ฅผ 70% ์ด์์ผ๋ก
}
}
else if (changeToNormal) {
if (server.memory_usage_percent >= 70) {
memory_delta = -this.getRandomInt(10, 30); // ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ํฌ๊ฒ ๊ฐ์
}
}
let memory_usage_percent = parseFloat((server.memory_usage_percent + memory_delta).toFixed(2));
memory_usage_percent = Math.max(5, Math.min(98, memory_usage_percent));
const memory_usage = Math.floor(server.memory_total * (memory_usage_percent / 100));
let disk_delta = parseFloat((this.getRandomInt(-5, 7) + timeInfluence * 0.4).toFixed(2)); // ๋์คํฌ๋ ์ฒ์ฒํ ์ฆ๊ฐ ๊ฒฝํฅ
// ์ํ ๋ณํ์ ๋ฐ๋ฅธ ๋์คํฌ ์ฌ์ฉ๋ ์กฐ์
if (changeToCritical) {
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 3) {
disk_delta = Math.max(90 - server.disk[0].disk_usage_percent, disk_delta); // ๋์คํฌ๋ฅผ 90% ์ด์์ผ๋ก
}
}
else if (changeToWarning) {
const resourceType = this.getRandomInt(1, 3);
if (resourceType === 3) {
disk_delta = Math.max(70 - server.disk[0].disk_usage_percent, disk_delta); // ๋์คํฌ๋ฅผ 70% ์ด์์ผ๋ก
}
}
else if (changeToNormal) {
if (server.disk[0].disk_usage_percent >= 70) {
disk_delta = -this.getRandomInt(5, 20); // ๋์คํฌ ์ฌ์ฉ๋ ๊ฐ์
}
}
let disk_usage_percent = parseFloat((server.disk[0].disk_usage_percent + disk_delta).toFixed(2));
disk_usage_percent = Math.max(5, Math.min(98, disk_usage_percent));
const disk_used = Math.floor(server.disk[0].disk_total * (disk_usage_percent / 100));
// ๋คํธ์ํฌ ํธ๋ํฝ ์
๋ฐ์ดํธ (์๊ฐ๋์ ํฌ๊ฒ ์ํฅ ๋ฐ์)
const trafficMultiplier = serverType === 'web' || serverType === 'api' ? 2 : 1; // ์น/API ์๋ฒ๋ ํธ๋ํฝ ๋ณ๋ ํญ์ด ๋ ํผ
const rx_delta = this.getRandomInt(-10, 20) * 1024 * 1024 * timeWeightMultiplier * trafficMultiplier;
const tx_delta = this.getRandomInt(-10, 20) * 1024 * 1024 * timeWeightMultiplier * trafficMultiplier;
const rx_bytes = Math.max(1024 * 1024, server.net.rx_bytes + rx_delta); // ์ต์ 1MB
const tx_bytes = Math.max(1024 * 1024, server.net.tx_bytes + tx_delta); // ์ต์ 1MB
// ๋คํธ์ํฌ ์ค๋ฅ ์
๋ฐ์ดํธ
const rx_errors = shouldUpdateErrors && Math.random() < this.errorProbability ?
server.net.rx_errors + this.getRandomInt(0, 10) :
Math.max(0, server.net.rx_errors - this.getRandomInt(0, 5));
const tx_errors = shouldUpdateErrors && Math.random() < this.errorProbability ?
server.net.tx_errors + this.getRandomInt(0, 5) :
Math.max(0, server.net.tx_errors - this.getRandomInt(0, 3));
// ์๋น์ค ์ํ ์
๋ฐ์ดํธ
let services = { ...server.services };
if (shouldUpdateServices) {
Object.keys(services).forEach(service => {
if (services[service] === 'stopped') {
// ์ค๋จ๋ ์๋น์ค๋ 70% ํ๋ฅ ๋ก ๋ณต๊ตฌ
services[service] = Math.random() < 0.7 ? 'running' : 'stopped';
} else {
// ์คํ ์ค์ธ ์๋น์ค๋ 20% ํ๋ฅ ๋ก ์ค๋จ
services[service] = Math.random() < 0.2 ? 'stopped' : 'running';
}
});
}
// ์ค๋ฅ ๋ฉ์์ง ์
๋ฐ์ดํธ
let errors = [...server.errors];
if (shouldUpdateErrors) {
// ๊ธฐ์กด ์ค๋ฅ ์ค 70%๋ ์ ๊ฑฐ(ํด๊ฒฐ๋จ)
errors = errors.filter(() => Math.random() > 0.7);
// ์ ์ค๋ฅ ์ถ๊ฐ
if (Math.random() < this.errorProbability) {
const errorCount = this.getRandomInt(1, 2);
for (let i = 0; i < errorCount; i++) {
errors.push(this.generateErrorMessage(serverType));
}
}
}
// ์ข๋น ํ๋ก์ธ์ค ์
๋ฐ์ดํธ
const zombie_count = Math.random() < 0.1 ? this.getRandomInt(1, 6) : 0;
// ์
๋ฐ์ดํธ๋ ์๋ฒ ๊ฐ์ฒด
const updatedServer = {
...server,
cpu_usage,
load_avg_1m: parseFloat((cpu_usage / 100 * this.getRandomInt(80, 120) / 100).toFixed(2)),
memory_usage,
memory_usage_percent,
disk: [{
...server.disk[0],
disk_used,
disk_usage_percent
}],
net: {
...server.net,
rx_bytes,
tx_bytes,
rx_errors,
tx_errors
},
services,
errors,
zombie_count,
timestamp: now.toISOString()
};
// ์ด๋ ฅ ๋ฐ์ดํฐ์ ์ถ๊ฐ
if (!this.historicalData[server.hostname]) {
this.historicalData[server.hostname] = [];
}
this.historicalData[server.hostname].push(updatedServer);
// ์ต๋ ๋ฐ์ดํฐ ํฌ์ธํธ ์ ์ ์ง (์ค๋๋ ๋ฐ์ดํฐ ์ ๊ฑฐ)
if (this.historicalData[server.hostname].length > this.maxHistoricalPoints) {
this.historicalData[server.hostname].shift();
}
return updatedServer;
});
// ์ ์ญ ๊ฐ์ฒด์ ์
๋ฐ์ดํธ๋ ๋ฐ์ดํฐ ํ ๋น
window.serverData = this.serverData;
window.serverHistoricalData = this.historicalData; // ์ด๋ ฅ ๋ฐ์ดํฐ๋ ์ ์ญ์ผ๋ก ์ ๊ณต
// ์ด๋ฒคํธ ๋ฐ์
this.dispatchUpdateEvent();
}
// ๋ฐ์ดํฐ ์
๋ฐ์ดํธ ์ด๋ฒคํธ ๋ฐ์
dispatchUpdateEvent() {
const event = new CustomEvent('serverDataUpdated', { detail: this.serverData });
window.dispatchEvent(event);
// ์ด๋ ฅ ๋ฐ์ดํฐ ์
๋ฐ์ดํธ ์ด๋ฒคํธ๋ ๋ฐ์
const historyEvent = new CustomEvent('serverHistoricalDataUpdated', { detail: this.historicalData });
window.dispatchEvent(historyEvent);
}
// ๋ฐ์ดํฐ ์
๋ฐ์ดํธ ์์
startDataUpdates() {
// ์ด๊ธฐ ๋ฐ์ดํฐ ์
๋ฐ์ดํธ
setTimeout(() => this.updateData(), 5000); // 5์ด ํ ์ฒซ ์
๋ฐ์ดํธ
// ์ฃผ๊ธฐ์ ์
๋ฐ์ดํธ ์ค์
setInterval(() => this.updateData(), this.updateInterval);
}
// ์๋ฒ ์ ํ๋ณ ๋ง์ถคํ ์ค๋ฅ ๋ฉ์์ง ์์ฑ
generateErrorMessage(serverType, severity = 'Warning', customMessage = null) {
const messages = {
Critical: [
`CRITICAL: Core service ${serverType.toUpperCase()}_SERVICE_01 failed to start. System integrity compromised. (systemctl status ${serverType.toLowerCase()}-service01 ํ์ธ ํ์)`,
`CRITICAL: Unrecoverable hardware error detected on ${serverType}. Immediate action required. (dmesg | grep -i hardware ํ์ธ ํ์)`,
`CRITICAL: Security breach attempt detected on ${serverType}! System locked down. (journalctl -p err ํ์ธ ํ์)`,
`CRITICAL: Kernel panic - not syncing: Fatal exception in interrupt on ${serverType} (dmesg | tail -50 ํ์ธ ํ์)`
],
Error: [
`ERROR: ${serverType.toUpperCase()}_APP_MODULE_X crashed due to an unhandled exception. (journalctl -u ${serverType.toLowerCase()}-app ํ์ธ ํ์)`,
`ERROR: Failed to connect to remote DB from ${serverType}. Timeout occurred. (ping DB_HOST ๋ฐ telnet DB_HOST DB_PORT ํ์ธ ํ์)`,
`ERROR: Configuration file for ${serverType.toUpperCase()}_SERVICE_02 is corrupted. (cat /etc/conf.d/${serverType.toLowerCase()}-service02.conf ํ์ธ ํ์)`,
`ERROR: High number of I/O errors on /dev/sdX on ${serverType}. Disk may be failing. (smartctl -a /dev/sdX ํ์ธ ํ์)`
],
Warning: [
`WARNING: High CPU load average on ${serverType} for the last 15 minutes. (top -b -n 1 ํ์ธ ํ์)`,
`WARNING: Memory usage on ${serverType} is approaching critical levels (85%). (free -m ํ์ธ ํ์)`,
`WARNING: Disk space on /var/log on ${serverType} is running low (currently 80% full). (df -h /var/log ํ์ธ ํ์)`,
`WARNING: Unexpected spike in network latency for ${serverType}. (ping -c 5 GATEWAY_IP ํ์ธ ํ์)`
]
};
if (customMessage) {
return `${severity.toUpperCase()}: ${customMessage} on ${serverType}. (journalctl -f ํ์ธ ํ์)`;
}
const severityMessages = messages[severity];
if (!severityMessages) return `${severity.toUpperCase()}: Unknown issue on ${serverType}. (journalctl -f ํ์ธ ํ์)`;
return this.getRandomItem(severityMessages);
}
// ์ ํธ๋ฆฌํฐ ํจ์
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
getRandomUsage(min, max) {
return parseFloat((Math.random() * (max - min) + min).toFixed(2));
}
getRandomItem(array) {
return array[Math.floor(Math.random() * array.length)];
}
getRandomItems(array, count) {
const shuffled = [...array].sort(() => 0.5 - Math.random());
return shuffled.slice(0, Math.min(count, array.length));
}
}
// ์ธ์คํด์ค ์์ฑ ๋ฐ ์ด๊ธฐํ
const dummyDataGenerator = new DummyDataGenerator();