gistfile1.txt
· 25 KiB · Text
Eredeti
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAID Capacity Calculator</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
.language-switcher {
text-align: right;
margin-bottom: 20px;
}
.language-switcher button {
background: none;
border: 1px solid #ccc;
padding: 5px 10px;
cursor: pointer;
margin-left: 5px;
}
.language-switcher button.active {
background: #eee;
}
.calculator-container {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.input-section {
flex: 1;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.results-section {
flex: 1;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f5f5f5;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select {
padding: 8px;
width: 100%;
box-sizing: border-box;
}
.result-item {
margin-bottom: 10px;
padding: 10px;
background-color: white;
border-radius: 3px;
}
.result-label {
font-weight: bold;
color: #555;
}
.hint-section {
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
margin-top: 20px;
}
.hint-title {
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.hint-content {
line-height: 1.5;
}
/* */
.language-switcher {
text-align: right;
margin-bottom: 20px;
}
.language-switcher button {
background: none;
border: 1px solid #ccc;
padding: 5px 10px;
cursor: pointer;
margin-left: 5px;
display: inline-flex;
align-items: center;
gap: 5px;
}
.language-switcher button:hover {
background-color: #f0f0f0;
}
.language-switcher button.active {
background: #eee;
font-weight: bold;
}
.language-switcher svg {
border: 1px solid #ddd;
}
</style>
</head>
<body>
<!--
<div class="language-switcher">
<button id="lang-en">English</button>
<button id="lang-ru">Русский</button>
</div>
-->
<div class="language-switcher">
<button id="lang-en" title="English">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10">
<clipPath id="s">
<path d="M0,0 v30 h60 v-30 z"/>
</clipPath>
<clipPath id="t">
<path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
</clipPath>
<g clip-path="url(#s)">
<path d="M0,0 v30 h60 v-30 z" fill="#012169"/>
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/>
<path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
<path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
</g>
<g clip-path="url(#t)">
<path d="M0,0 v30 h60 v-30 z" fill="#012169"/>
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/>
<path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
<path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
</g>
</svg>
<span>English</span>
</button>
<button id="lang-ru" title="Русский">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10">
<clipPath id="r">
<path d="M0,0 v30 h60 v-30 z"/>
</clipPath>
<g clip-path="url(#r)">
<path d="M0,0 h60 v10 h-60 z" fill="#fff"/>
<path d="M0,10 h60 v10 h-60 z" fill="#0039A6"/>
<path d="M0,20 h60 v10 h-60 z" fill="#D52B1E"/>
</g>
</svg>
<span>Русский</span>
</button>
</div>
<h1 id="title">RAID Capacity Calculator</h1>
<div class="calculator-container">
<div class="input-section">
<div class="input-group">
<label for="disk-count" id="disk-count-label">Number of disks:</label>
<input type="number" id="disk-count" min="1" value="4">
</div>
<div class="input-group">
<label for="disk-size" id="disk-size-label">Disk size (GB):</label>
<input type="number" id="disk-size" min="1" value="1000">
</div>
<div class="input-group">
<label for="raid-type" id="raid-type-label">RAID type:</label>
<select id="raid-type">
<optgroup label="Standard RAID">
<option value="raid0">RAID 0</option>
<option value="raid1">RAID 1</option>
<option value="raid3">RAID 3</option>
<option value="raid5">RAID 5</option>
<option value="raid6">RAID 6</option>
<option value="raid10">RAID 10</option>
</optgroup>
<optgroup label="ZFS">
<option value="zfsStripe">Stripe set</option>
<option value="zfsMirror">Mirror</option>
<option value="zfsZ1">RAID-Z1</option>
<option value="zfsZ2">RAID-Z2</option>
<option value="zfsZ3">RAID-Z3</option>
</optgroup>
</select>
</div>
</div>
<div class="results-section">
<h2 id="results-title">Results</h2>
<div class="result-item">
<div class="result-label" id="total-space-label">Total space:</div>
<div id="total-space">0</div>
</div>
<div class="result-item">
<div class="result-label" id="effective-space-label">Effective space:</div>
<div id="effective-space">0</div>
</div>
<div class="result-item">
<div class="result-label" id="efficiency-label">Efficiency:</div>
<div id="efficiency">0</div>
</div>
<div class="result-item">
<div class="result-label" id="fault-tolerance-label">Fault tolerance:</div>
<div id="fault-tolerance">None</div>
</div>
</div>
</div>
<div class="hint-section">
<div class="hint-title" id="hint-title">About selected RAID type:</div>
<div class="hint-content" id="hint-content"></div>
</div>
<script>
// Language data
const translations = {
en: {
title: "RAID Capacity Calculator",
diskCountLabel: "Number of disks:",
diskSizeLabel: "Disk size (GB):",
raidTypeLabel: "RAID type:",
resultsTitle: "Results",
totalSpaceLabel: "Total space:",
effectiveSpaceLabel: "Effective space:",
efficiencyLabel: "Efficiency:",
faultToleranceLabel: "Fault tolerance:",
hintTitle: "About selected RAID type:",
raidTypes: {
standard: "Standard RAID",
zfs: "ZFS",
raid0: "RAID 0",
raid1: "RAID 1",
raid3: "RAID 3",
raid5: "RAID 5",
raid6: "RAID 6",
raid10: "RAID 10",
zfsStripe: "Stripe set",
zfsMirror: "Mirror",
zfsZ1: "RAID-Z1",
zfsZ2: "RAID-Z2",
zfsZ3: "RAID-Z3"
},
faultTolerance: {
none: "None",
one: "1 disk failure",
two: "2 disk failures",
three: "3 disk failures",
half: "Up to half of disks (in mirrored pairs)"
},
hints: {
raid0: "RAID 0 (striping) combines multiple disks into one logical unit with data distributed across all disks. Provides improved performance but no redundancy - failure of any disk results in total data loss.",
raid1: "RAID 1 (mirroring) creates an exact copy of data on two or more disks. Provides fault tolerance but at the cost of reduced storage capacity.",
raid3: "RAID 3 uses byte-level striping with a dedicated parity disk. Good for sequential data access but poor for random access. Can tolerate one disk failure.",
raid5: "RAID 5 uses block-level striping with distributed parity. Provides good balance between performance, capacity and fault tolerance. Can tolerate one disk failure.",
raid6: "RAID 6 is similar to RAID 5 but with double distributed parity. Can tolerate two disk failures, making it more reliable for large arrays.",
raid10: "RAID 10 (1+0) combines mirroring and striping. Requires even number of disks (minimum 4). Provides good performance and can tolerate multiple disk failures (one per mirrored pair).",
zfsStripe: "ZFS stripe is similar to RAID 0 - no redundancy but maximum capacity and performance. Data is distributed across all disks in the pool.",
zfsMirror: "ZFS mirror is similar to RAID 1 - data is duplicated on two or more disks. Provides redundancy at the cost of storage capacity.",
zfsZ1: "RAID-Z1 is similar to RAID 5 - single parity with distributed striping. Can tolerate one disk failure while providing good capacity and performance.",
zfsZ2: "RAID-Z2 is similar to RAID 6 - double parity with distributed striping. Can tolerate two disk failures, recommended for larger pools.",
zfsZ3: "RAID-Z3 provides triple parity protection, able to withstand three disk failures. Recommended for very large pools where rebuild times are long."
}
},
ru: {
title: "Калькулятор ёмкости RAID",
diskCountLabel: "Число дисков:",
diskSizeLabel: "Размер диска (ГБ):",
raidTypeLabel: "Тип RAID:",
resultsTitle: "Результаты",
totalSpaceLabel: "Общий объем:",
effectiveSpaceLabel: "Эффективный объем:",
efficiencyLabel: "Эффективность использования:",
faultToleranceLabel: "Отказоустойчивость:",
hintTitle: "О выбранном типе RAID:",
raidTypes: {
standard: "Обычный RAID",
zfs: "ZFS",
raid0: "RAID 0",
raid1: "RAID 1",
raid3: "RAID 3",
raid5: "RAID 5",
raid6: "RAID 6",
raid10: "RAID 10",
zfsStripe: "Stripe set",
zfsMirror: "Mirror",
zfsZ1: "RAID-Z1",
zfsZ2: "RAID-Z2",
zfsZ3: "RAID-Z3"
},
faultTolerance: {
none: "Нет",
one: "1 отказ диска",
two: "2 отказа диска",
three: "3 отказа диска",
half: "До половины дисков (в зеркальных парах)"
},
hints: {
raid0: "RAID 0 (чередование) объединяет несколько дисков в один логический блок с распределением данных по всем дискам. Обеспечивает повышенную производительность, но не имеет избыточности - отказ любого диска приводит к полной потере данных.",
raid1: "RAID 1 (зеркалирование) создает точную копию данных на двух или более дисках. Обеспечивает отказоустойчивость, но за счет уменьшения полезного объема хранилища.",
raid3: "RAID 3 использует чередование на уровне байтов с выделенным диском четности. Хорошо подходит для последовательного доступа к данным, но плохо для случайного. Может выдержать отказ одного диска.",
raid5: "RAID 5 использует чередование на уровне блоков с распределенной четностью. Обеспечивает хороший баланс между производительностью, емкостью и отказоустойчивостью. Может выдержать отказ одного диска.",
raid6: "RAID 6 аналогичен RAID 5, но с двойной распределенной четностью. Может выдержать отказ двух дисков, что делает его более надежным для больших массивов.",
raid10: "RAID 10 (1+0) сочетает зеркалирование и чередование. Требует четного количества дисков (минимум 4). Обеспечивает хорошую производительность и может выдержать отказ нескольких дисков (по одному в каждой зеркальной паре).",
zfsStripe: "ZFS stripe аналогичен RAID 0 - нет избыточности, но максимальная емкость и производительность. Данные распределяются по всем дискам в пуле.",
zfsMirror: "ZFS mirror аналогичен RAID 1 - данные дублируются на двух или более дисках. Обеспечивает избыточность за счет полезного объема хранилища.",
zfsZ1: "RAID-Z1 аналогичен RAID 5 - одинарная четность с распределенным чередованием. Может выдержать отказ одного диска при хорошей емкости и производительности.",
zfsZ2: "RAID-Z2 аналогичен RAID 6 - двойная четность с распределенным чередованием. Может выдержать отказ двух дисков, рекомендуется для больших пулов.",
zfsZ3: "RAID-Z3 обеспечивает тройную защиту четностью, может выдержать отказ трех дисков. Рекомендуется для очень больших пулов, где время восстановления велико."
}
}
};
// Current language
let currentLang = 'en';
// DOM elements
const elements = {
title: document.getElementById('title'),
diskCountLabel: document.getElementById('disk-count-label'),
diskSizeLabel: document.getElementById('disk-size-label'),
raidTypeLabel: document.getElementById('raid-type-label'),
resultsTitle: document.getElementById('results-title'),
totalSpaceLabel: document.getElementById('total-space-label'),
effectiveSpaceLabel: document.getElementById('effective-space-label'),
efficiencyLabel: document.getElementById('efficiency-label'),
faultToleranceLabel: document.getElementById('fault-tolerance-label'),
hintTitle: document.getElementById('hint-title'),
hintContent: document.getElementById('hint-content'),
raidTypeSelect: document.getElementById('raid-type'),
diskCountInput: document.getElementById('disk-count'),
diskSizeInput: document.getElementById('disk-size'),
raidTypeOptgroups: document.querySelectorAll('#raid-type optgroup'),
raidTypeOptions: document.querySelectorAll('#raid-type option')
};
// Save state to URL hash
function saveStateToHash() {
const params = new URLSearchParams();
params.set('lang', currentLang);
params.set('count', elements.diskCountInput.value);
params.set('size', elements.diskSizeInput.value);
params.set('raid', elements.raidTypeSelect.value);
window.location.hash = params.toString();
}
// Load state from URL hash
function loadStateFromHash() {
if (window.location.hash) {
try {
const hash = window.location.hash.substring(1);
const params = new URLSearchParams(hash);
if (params.has('lang') && translations[params.get('lang')]) {
currentLang = params.get('lang');
}
if (params.has('count')) {
elements.diskCountInput.value = params.get('count');
}
if (params.has('size')) {
elements.diskSizeInput.value = params.get('size');
}
if (params.has('raid')) {
elements.raidTypeSelect.value = params.get('raid');
}
return true;
} catch (e) {
console.error("Error parsing hash:", e);
}
}
return false;
}
// Update UI language
function updateLanguage(lang) {
currentLang = lang;
const t = translations[lang];
// Update labels
elements.title.textContent = t.title;
elements.diskCountLabel.textContent = t.diskCountLabel;
elements.diskSizeLabel.textContent = t.diskSizeLabel;
elements.raidTypeLabel.textContent = t.raidTypeLabel;
elements.resultsTitle.textContent = t.resultsTitle;
elements.totalSpaceLabel.textContent = t.totalSpaceLabel;
elements.effectiveSpaceLabel.textContent = t.effectiveSpaceLabel;
elements.efficiencyLabel.textContent = t.efficiencyLabel;
elements.faultToleranceLabel.textContent = t.faultToleranceLabel;
elements.hintTitle.textContent = t.hintTitle;
// Update RAID type options
elements.raidTypeOptgroups[0].label = t.raidTypes.standard;
elements.raidTypeOptgroups[1].label = t.raidTypes.zfs;
const raidTypeOptions = [
t.raidTypes.raid0, t.raidTypes.raid1, t.raidTypes.raid3,
t.raidTypes.raid5, t.raidTypes.raid6, t.raidTypes.raid10,
t.raidTypes.zfsStripe, t.raidTypes.zfsMirror,
t.raidTypes.zfsZ1, t.raidTypes.zfsZ2, t.raidTypes.zfsZ3
];
elements.raidTypeOptions.forEach((option, index) => {
option.textContent = raidTypeOptions[index];
});
// Recalculate to update fault tolerance text and hint
calculate();
}
// Update hint based on selected RAID type
function updateHint(raidType) {
elements.hintContent.textContent = translations[currentLang].hints[raidType] || '';
}
// Calculate RAID parameters
function calculate() {
const diskCount = parseInt(elements.diskCountInput.value) || 0;
const diskSize = parseInt(elements.diskSizeInput.value) || 0;
const raidType = elements.raidTypeSelect.value;
let totalSpace, effectiveSpace, efficiency, faultTolerance;
const t = translations[currentLang].faultTolerance;
switch(raidType) {
case 'raid0':
case 'zfsStripe':
totalSpace = diskCount * diskSize;
effectiveSpace = totalSpace;
efficiency = 100;
faultTolerance = t.none;
break;
case 'raid1':
case 'zfsMirror':
totalSpace = diskCount * diskSize;
effectiveSpace = diskSize;
efficiency = (100 / diskCount).toFixed(2);
faultTolerance = diskCount > 2 ? t.half : t.one;
break;
case 'raid3':
case 'raid5':
case 'zfsZ1':
totalSpace = diskCount * diskSize;
effectiveSpace = (diskCount - 1) * diskSize;
efficiency = ((diskCount - 1) / diskCount * 100).toFixed(2);
faultTolerance = t.one;
break;
case 'raid6':
case 'zfsZ2':
totalSpace = diskCount * diskSize;
effectiveSpace = (diskCount - 2) * diskSize;
efficiency = ((diskCount - 2) / diskCount * 100).toFixed(2);
faultTolerance = t.two;
break;
case 'zfsZ3':
totalSpace = diskCount * diskSize;
effectiveSpace = (diskCount - 3) * diskSize;
efficiency = ((diskCount - 3) / diskCount * 100).toFixed(2);
faultTolerance = t.three;
break;
case 'raid10':
if (diskCount < 2 || diskCount % 2 !== 0) {
effectiveSpace = 0;
efficiency = 0;
faultTolerance = t.none;
} else {
totalSpace = diskCount * diskSize;
effectiveSpace = (diskCount / 2) * diskSize;
efficiency = 50;
faultTolerance = t.half;
}
break;
default:
totalSpace = effectiveSpace = efficiency = 0;
faultTolerance = t.none;
}
// Update results
document.getElementById('total-space').textContent = totalSpace.toLocaleString() + ' Gb';
document.getElementById('effective-space').textContent = effectiveSpace.toLocaleString() + ' Gb';
document.getElementById('efficiency').textContent = efficiency + ' %';
document.getElementById('fault-tolerance').textContent = faultTolerance;
// Update hint
updateHint(raidType);
// Save state
saveStateToHash();
}
// Event listeners
function setupEventListeners() {
elements.diskCountInput.addEventListener('input', calculate);
elements.diskSizeInput.addEventListener('input', calculate);
elements.raidTypeSelect.addEventListener('change', calculate);
document.getElementById('lang-en').addEventListener('click', () => {
currentLang = 'en';
updateLanguage('en');
saveStateToHash();
});
document.getElementById('lang-ru').addEventListener('click', () => {
currentLang = 'ru';
updateLanguage('ru');
saveStateToHash();
});
}
// Initialize
function init() {
// Try to load state from hash
const stateLoaded = loadStateFromHash();
// Setup event listeners
setupEventListeners();
// Update UI with current or default values
updateLanguage(currentLang);
// If no state was loaded, calculate with defaults
if (!stateLoaded) {
calculate();
}
}
// Start the application
init();
</script>
</body>
</html>
1 | <!DOCTYPE html> |
2 | <html lang="en"> |
3 | <head> |
4 | <meta charset="UTF-8"> |
5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
6 | <title>RAID Capacity Calculator</title> |
7 | <style> |
8 | body { |
9 | font-family: Arial, sans-serif; |
10 | max-width: 1000px; |
11 | margin: 0 auto; |
12 | padding: 20px; |
13 | line-height: 1.6; |
14 | } |
15 | .language-switcher { |
16 | text-align: right; |
17 | margin-bottom: 20px; |
18 | } |
19 | .language-switcher button { |
20 | background: none; |
21 | border: 1px solid #ccc; |
22 | padding: 5px 10px; |
23 | cursor: pointer; |
24 | margin-left: 5px; |
25 | } |
26 | .language-switcher button.active { |
27 | background: #eee; |
28 | } |
29 | .calculator-container { |
30 | display: flex; |
31 | gap: 20px; |
32 | margin-bottom: 30px; |
33 | } |
34 | .input-section { |
35 | flex: 1; |
36 | padding: 15px; |
37 | border: 1px solid #ddd; |
38 | border-radius: 5px; |
39 | } |
40 | .results-section { |
41 | flex: 1; |
42 | padding: 15px; |
43 | border: 1px solid #ddd; |
44 | border-radius: 5px; |
45 | background-color: #f5f5f5; |
46 | } |
47 | .input-group { |
48 | margin-bottom: 15px; |
49 | } |
50 | label { |
51 | display: block; |
52 | margin-bottom: 5px; |
53 | font-weight: bold; |
54 | } |
55 | input, select { |
56 | padding: 8px; |
57 | width: 100%; |
58 | box-sizing: border-box; |
59 | } |
60 | .result-item { |
61 | margin-bottom: 10px; |
62 | padding: 10px; |
63 | background-color: white; |
64 | border-radius: 3px; |
65 | } |
66 | .result-label { |
67 | font-weight: bold; |
68 | color: #555; |
69 | } |
70 | .hint-section { |
71 | padding: 20px; |
72 | border: 1px solid #ddd; |
73 | border-radius: 5px; |
74 | background-color: #f9f9f9; |
75 | margin-top: 20px; |
76 | } |
77 | .hint-title { |
78 | font-weight: bold; |
79 | margin-bottom: 10px; |
80 | color: #333; |
81 | } |
82 | .hint-content { |
83 | line-height: 1.5; |
84 | } |
85 | |
86 | /* */ |
87 | .language-switcher { |
88 | text-align: right; |
89 | margin-bottom: 20px; |
90 | } |
91 | .language-switcher button { |
92 | background: none; |
93 | border: 1px solid #ccc; |
94 | padding: 5px 10px; |
95 | cursor: pointer; |
96 | margin-left: 5px; |
97 | display: inline-flex; |
98 | align-items: center; |
99 | gap: 5px; |
100 | } |
101 | .language-switcher button:hover { |
102 | background-color: #f0f0f0; |
103 | } |
104 | .language-switcher button.active { |
105 | background: #eee; |
106 | font-weight: bold; |
107 | } |
108 | .language-switcher svg { |
109 | border: 1px solid #ddd; |
110 | } |
111 | |
112 | </style> |
113 | </head> |
114 | <body> |
115 | <!-- |
116 | <div class="language-switcher"> |
117 | <button id="lang-en">English</button> |
118 | <button id="lang-ru">Русский</button> |
119 | </div> |
120 | --> |
121 | <div class="language-switcher"> |
122 | <button id="lang-en" title="English"> |
123 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10"> |
124 | <clipPath id="s"> |
125 | <path d="M0,0 v30 h60 v-30 z"/> |
126 | </clipPath> |
127 | <clipPath id="t"> |
128 | <path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/> |
129 | </clipPath> |
130 | <g clip-path="url(#s)"> |
131 | <path d="M0,0 v30 h60 v-30 z" fill="#012169"/> |
132 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/> |
133 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/> |
134 | <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/> |
135 | <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/> |
136 | </g> |
137 | <g clip-path="url(#t)"> |
138 | <path d="M0,0 v30 h60 v-30 z" fill="#012169"/> |
139 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/> |
140 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/> |
141 | <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/> |
142 | <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/> |
143 | </g> |
144 | </svg> |
145 | <span>English</span> |
146 | </button> |
147 | <button id="lang-ru" title="Русский"> |
148 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10"> |
149 | <clipPath id="r"> |
150 | <path d="M0,0 v30 h60 v-30 z"/> |
151 | </clipPath> |
152 | <g clip-path="url(#r)"> |
153 | <path d="M0,0 h60 v10 h-60 z" fill="#fff"/> |
154 | <path d="M0,10 h60 v10 h-60 z" fill="#0039A6"/> |
155 | <path d="M0,20 h60 v10 h-60 z" fill="#D52B1E"/> |
156 | </g> |
157 | </svg> |
158 | <span>Русский</span> |
159 | </button> |
160 | </div> |
161 | |
162 | <h1 id="title">RAID Capacity Calculator</h1> |
163 | |
164 | <div class="calculator-container"> |
165 | <div class="input-section"> |
166 | <div class="input-group"> |
167 | <label for="disk-count" id="disk-count-label">Number of disks:</label> |
168 | <input type="number" id="disk-count" min="1" value="4"> |
169 | </div> |
170 | |
171 | <div class="input-group"> |
172 | <label for="disk-size" id="disk-size-label">Disk size (GB):</label> |
173 | <input type="number" id="disk-size" min="1" value="1000"> |
174 | </div> |
175 | |
176 | <div class="input-group"> |
177 | <label for="raid-type" id="raid-type-label">RAID type:</label> |
178 | <select id="raid-type"> |
179 | <optgroup label="Standard RAID"> |
180 | <option value="raid0">RAID 0</option> |
181 | <option value="raid1">RAID 1</option> |
182 | <option value="raid3">RAID 3</option> |
183 | <option value="raid5">RAID 5</option> |
184 | <option value="raid6">RAID 6</option> |
185 | <option value="raid10">RAID 10</option> |
186 | </optgroup> |
187 | <optgroup label="ZFS"> |
188 | <option value="zfsStripe">Stripe set</option> |
189 | <option value="zfsMirror">Mirror</option> |
190 | <option value="zfsZ1">RAID-Z1</option> |
191 | <option value="zfsZ2">RAID-Z2</option> |
192 | <option value="zfsZ3">RAID-Z3</option> |
193 | </optgroup> |
194 | </select> |
195 | </div> |
196 | </div> |
197 | |
198 | <div class="results-section"> |
199 | <h2 id="results-title">Results</h2> |
200 | <div class="result-item"> |
201 | <div class="result-label" id="total-space-label">Total space:</div> |
202 | <div id="total-space">0</div> |
203 | </div> |
204 | <div class="result-item"> |
205 | <div class="result-label" id="effective-space-label">Effective space:</div> |
206 | <div id="effective-space">0</div> |
207 | </div> |
208 | <div class="result-item"> |
209 | <div class="result-label" id="efficiency-label">Efficiency:</div> |
210 | <div id="efficiency">0</div> |
211 | </div> |
212 | <div class="result-item"> |
213 | <div class="result-label" id="fault-tolerance-label">Fault tolerance:</div> |
214 | <div id="fault-tolerance">None</div> |
215 | </div> |
216 | </div> |
217 | </div> |
218 | |
219 | <div class="hint-section"> |
220 | <div class="hint-title" id="hint-title">About selected RAID type:</div> |
221 | <div class="hint-content" id="hint-content"></div> |
222 | </div> |
223 | |
224 | <script> |
225 | // Language data |
226 | const translations = { |
227 | en: { |
228 | title: "RAID Capacity Calculator", |
229 | diskCountLabel: "Number of disks:", |
230 | diskSizeLabel: "Disk size (GB):", |
231 | raidTypeLabel: "RAID type:", |
232 | resultsTitle: "Results", |
233 | totalSpaceLabel: "Total space:", |
234 | effectiveSpaceLabel: "Effective space:", |
235 | efficiencyLabel: "Efficiency:", |
236 | faultToleranceLabel: "Fault tolerance:", |
237 | hintTitle: "About selected RAID type:", |
238 | raidTypes: { |
239 | standard: "Standard RAID", |
240 | zfs: "ZFS", |
241 | raid0: "RAID 0", |
242 | raid1: "RAID 1", |
243 | raid3: "RAID 3", |
244 | raid5: "RAID 5", |
245 | raid6: "RAID 6", |
246 | raid10: "RAID 10", |
247 | zfsStripe: "Stripe set", |
248 | zfsMirror: "Mirror", |
249 | zfsZ1: "RAID-Z1", |
250 | zfsZ2: "RAID-Z2", |
251 | zfsZ3: "RAID-Z3" |
252 | }, |
253 | faultTolerance: { |
254 | none: "None", |
255 | one: "1 disk failure", |
256 | two: "2 disk failures", |
257 | three: "3 disk failures", |
258 | half: "Up to half of disks (in mirrored pairs)" |
259 | }, |
260 | hints: { |
261 | raid0: "RAID 0 (striping) combines multiple disks into one logical unit with data distributed across all disks. Provides improved performance but no redundancy - failure of any disk results in total data loss.", |
262 | raid1: "RAID 1 (mirroring) creates an exact copy of data on two or more disks. Provides fault tolerance but at the cost of reduced storage capacity.", |
263 | raid3: "RAID 3 uses byte-level striping with a dedicated parity disk. Good for sequential data access but poor for random access. Can tolerate one disk failure.", |
264 | raid5: "RAID 5 uses block-level striping with distributed parity. Provides good balance between performance, capacity and fault tolerance. Can tolerate one disk failure.", |
265 | raid6: "RAID 6 is similar to RAID 5 but with double distributed parity. Can tolerate two disk failures, making it more reliable for large arrays.", |
266 | raid10: "RAID 10 (1+0) combines mirroring and striping. Requires even number of disks (minimum 4). Provides good performance and can tolerate multiple disk failures (one per mirrored pair).", |
267 | zfsStripe: "ZFS stripe is similar to RAID 0 - no redundancy but maximum capacity and performance. Data is distributed across all disks in the pool.", |
268 | zfsMirror: "ZFS mirror is similar to RAID 1 - data is duplicated on two or more disks. Provides redundancy at the cost of storage capacity.", |
269 | zfsZ1: "RAID-Z1 is similar to RAID 5 - single parity with distributed striping. Can tolerate one disk failure while providing good capacity and performance.", |
270 | zfsZ2: "RAID-Z2 is similar to RAID 6 - double parity with distributed striping. Can tolerate two disk failures, recommended for larger pools.", |
271 | zfsZ3: "RAID-Z3 provides triple parity protection, able to withstand three disk failures. Recommended for very large pools where rebuild times are long." |
272 | } |
273 | }, |
274 | ru: { |
275 | title: "Калькулятор ёмкости RAID", |
276 | diskCountLabel: "Число дисков:", |
277 | diskSizeLabel: "Размер диска (ГБ):", |
278 | raidTypeLabel: "Тип RAID:", |
279 | resultsTitle: "Результаты", |
280 | totalSpaceLabel: "Общий объем:", |
281 | effectiveSpaceLabel: "Эффективный объем:", |
282 | efficiencyLabel: "Эффективность использования:", |
283 | faultToleranceLabel: "Отказоустойчивость:", |
284 | hintTitle: "О выбранном типе RAID:", |
285 | raidTypes: { |
286 | standard: "Обычный RAID", |
287 | zfs: "ZFS", |
288 | raid0: "RAID 0", |
289 | raid1: "RAID 1", |
290 | raid3: "RAID 3", |
291 | raid5: "RAID 5", |
292 | raid6: "RAID 6", |
293 | raid10: "RAID 10", |
294 | zfsStripe: "Stripe set", |
295 | zfsMirror: "Mirror", |
296 | zfsZ1: "RAID-Z1", |
297 | zfsZ2: "RAID-Z2", |
298 | zfsZ3: "RAID-Z3" |
299 | }, |
300 | faultTolerance: { |
301 | none: "Нет", |
302 | one: "1 отказ диска", |
303 | two: "2 отказа диска", |
304 | three: "3 отказа диска", |
305 | half: "До половины дисков (в зеркальных парах)" |
306 | }, |
307 | hints: { |
308 | raid0: "RAID 0 (чередование) объединяет несколько дисков в один логический блок с распределением данных по всем дискам. Обеспечивает повышенную производительность, но не имеет избыточности - отказ любого диска приводит к полной потере данных.", |
309 | raid1: "RAID 1 (зеркалирование) создает точную копию данных на двух или более дисках. Обеспечивает отказоустойчивость, но за счет уменьшения полезного объема хранилища.", |
310 | raid3: "RAID 3 использует чередование на уровне байтов с выделенным диском четности. Хорошо подходит для последовательного доступа к данным, но плохо для случайного. Может выдержать отказ одного диска.", |
311 | raid5: "RAID 5 использует чередование на уровне блоков с распределенной четностью. Обеспечивает хороший баланс между производительностью, емкостью и отказоустойчивостью. Может выдержать отказ одного диска.", |
312 | raid6: "RAID 6 аналогичен RAID 5, но с двойной распределенной четностью. Может выдержать отказ двух дисков, что делает его более надежным для больших массивов.", |
313 | raid10: "RAID 10 (1+0) сочетает зеркалирование и чередование. Требует четного количества дисков (минимум 4). Обеспечивает хорошую производительность и может выдержать отказ нескольких дисков (по одному в каждой зеркальной паре).", |
314 | zfsStripe: "ZFS stripe аналогичен RAID 0 - нет избыточности, но максимальная емкость и производительность. Данные распределяются по всем дискам в пуле.", |
315 | zfsMirror: "ZFS mirror аналогичен RAID 1 - данные дублируются на двух или более дисках. Обеспечивает избыточность за счет полезного объема хранилища.", |
316 | zfsZ1: "RAID-Z1 аналогичен RAID 5 - одинарная четность с распределенным чередованием. Может выдержать отказ одного диска при хорошей емкости и производительности.", |
317 | zfsZ2: "RAID-Z2 аналогичен RAID 6 - двойная четность с распределенным чередованием. Может выдержать отказ двух дисков, рекомендуется для больших пулов.", |
318 | zfsZ3: "RAID-Z3 обеспечивает тройную защиту четностью, может выдержать отказ трех дисков. Рекомендуется для очень больших пулов, где время восстановления велико." |
319 | } |
320 | } |
321 | }; |
322 | |
323 | // Current language |
324 | let currentLang = 'en'; |
325 | |
326 | // DOM elements |
327 | const elements = { |
328 | title: document.getElementById('title'), |
329 | diskCountLabel: document.getElementById('disk-count-label'), |
330 | diskSizeLabel: document.getElementById('disk-size-label'), |
331 | raidTypeLabel: document.getElementById('raid-type-label'), |
332 | resultsTitle: document.getElementById('results-title'), |
333 | totalSpaceLabel: document.getElementById('total-space-label'), |
334 | effectiveSpaceLabel: document.getElementById('effective-space-label'), |
335 | efficiencyLabel: document.getElementById('efficiency-label'), |
336 | faultToleranceLabel: document.getElementById('fault-tolerance-label'), |
337 | hintTitle: document.getElementById('hint-title'), |
338 | hintContent: document.getElementById('hint-content'), |
339 | raidTypeSelect: document.getElementById('raid-type'), |
340 | diskCountInput: document.getElementById('disk-count'), |
341 | diskSizeInput: document.getElementById('disk-size'), |
342 | raidTypeOptgroups: document.querySelectorAll('#raid-type optgroup'), |
343 | raidTypeOptions: document.querySelectorAll('#raid-type option') |
344 | }; |
345 | |
346 | // Save state to URL hash |
347 | function saveStateToHash() { |
348 | const params = new URLSearchParams(); |
349 | params.set('lang', currentLang); |
350 | params.set('count', elements.diskCountInput.value); |
351 | params.set('size', elements.diskSizeInput.value); |
352 | params.set('raid', elements.raidTypeSelect.value); |
353 | |
354 | window.location.hash = params.toString(); |
355 | } |
356 | |
357 | |
358 | // Load state from URL hash |
359 | function loadStateFromHash() { |
360 | if (window.location.hash) { |
361 | try { |
362 | const hash = window.location.hash.substring(1); |
363 | const params = new URLSearchParams(hash); |
364 | |
365 | if (params.has('lang') && translations[params.get('lang')]) { |
366 | currentLang = params.get('lang'); |
367 | } |
368 | if (params.has('count')) { |
369 | elements.diskCountInput.value = params.get('count'); |
370 | } |
371 | if (params.has('size')) { |
372 | elements.diskSizeInput.value = params.get('size'); |
373 | } |
374 | if (params.has('raid')) { |
375 | elements.raidTypeSelect.value = params.get('raid'); |
376 | } |
377 | |
378 | return true; |
379 | } catch (e) { |
380 | console.error("Error parsing hash:", e); |
381 | } |
382 | } |
383 | return false; |
384 | } |
385 | |
386 | // Update UI language |
387 | function updateLanguage(lang) { |
388 | currentLang = lang; |
389 | const t = translations[lang]; |
390 | |
391 | // Update labels |
392 | elements.title.textContent = t.title; |
393 | elements.diskCountLabel.textContent = t.diskCountLabel; |
394 | elements.diskSizeLabel.textContent = t.diskSizeLabel; |
395 | elements.raidTypeLabel.textContent = t.raidTypeLabel; |
396 | elements.resultsTitle.textContent = t.resultsTitle; |
397 | elements.totalSpaceLabel.textContent = t.totalSpaceLabel; |
398 | elements.effectiveSpaceLabel.textContent = t.effectiveSpaceLabel; |
399 | elements.efficiencyLabel.textContent = t.efficiencyLabel; |
400 | elements.faultToleranceLabel.textContent = t.faultToleranceLabel; |
401 | elements.hintTitle.textContent = t.hintTitle; |
402 | |
403 | // Update RAID type options |
404 | elements.raidTypeOptgroups[0].label = t.raidTypes.standard; |
405 | elements.raidTypeOptgroups[1].label = t.raidTypes.zfs; |
406 | |
407 | const raidTypeOptions = [ |
408 | t.raidTypes.raid0, t.raidTypes.raid1, t.raidTypes.raid3, |
409 | t.raidTypes.raid5, t.raidTypes.raid6, t.raidTypes.raid10, |
410 | t.raidTypes.zfsStripe, t.raidTypes.zfsMirror, |
411 | t.raidTypes.zfsZ1, t.raidTypes.zfsZ2, t.raidTypes.zfsZ3 |
412 | ]; |
413 | |
414 | elements.raidTypeOptions.forEach((option, index) => { |
415 | option.textContent = raidTypeOptions[index]; |
416 | }); |
417 | |
418 | // Recalculate to update fault tolerance text and hint |
419 | calculate(); |
420 | } |
421 | |
422 | // Update hint based on selected RAID type |
423 | function updateHint(raidType) { |
424 | elements.hintContent.textContent = translations[currentLang].hints[raidType] || ''; |
425 | } |
426 | |
427 | // Calculate RAID parameters |
428 | function calculate() { |
429 | const diskCount = parseInt(elements.diskCountInput.value) || 0; |
430 | const diskSize = parseInt(elements.diskSizeInput.value) || 0; |
431 | const raidType = elements.raidTypeSelect.value; |
432 | |
433 | let totalSpace, effectiveSpace, efficiency, faultTolerance; |
434 | const t = translations[currentLang].faultTolerance; |
435 | |
436 | switch(raidType) { |
437 | case 'raid0': |
438 | case 'zfsStripe': |
439 | totalSpace = diskCount * diskSize; |
440 | effectiveSpace = totalSpace; |
441 | efficiency = 100; |
442 | faultTolerance = t.none; |
443 | break; |
444 | |
445 | case 'raid1': |
446 | case 'zfsMirror': |
447 | totalSpace = diskCount * diskSize; |
448 | effectiveSpace = diskSize; |
449 | efficiency = (100 / diskCount).toFixed(2); |
450 | faultTolerance = diskCount > 2 ? t.half : t.one; |
451 | break; |
452 | |
453 | case 'raid3': |
454 | case 'raid5': |
455 | case 'zfsZ1': |
456 | totalSpace = diskCount * diskSize; |
457 | effectiveSpace = (diskCount - 1) * diskSize; |
458 | efficiency = ((diskCount - 1) / diskCount * 100).toFixed(2); |
459 | faultTolerance = t.one; |
460 | break; |
461 | |
462 | case 'raid6': |
463 | case 'zfsZ2': |
464 | totalSpace = diskCount * diskSize; |
465 | effectiveSpace = (diskCount - 2) * diskSize; |
466 | efficiency = ((diskCount - 2) / diskCount * 100).toFixed(2); |
467 | faultTolerance = t.two; |
468 | break; |
469 | |
470 | case 'zfsZ3': |
471 | totalSpace = diskCount * diskSize; |
472 | effectiveSpace = (diskCount - 3) * diskSize; |
473 | efficiency = ((diskCount - 3) / diskCount * 100).toFixed(2); |
474 | faultTolerance = t.three; |
475 | break; |
476 | |
477 | case 'raid10': |
478 | if (diskCount < 2 || diskCount % 2 !== 0) { |
479 | effectiveSpace = 0; |
480 | efficiency = 0; |
481 | faultTolerance = t.none; |
482 | } else { |
483 | totalSpace = diskCount * diskSize; |
484 | effectiveSpace = (diskCount / 2) * diskSize; |
485 | efficiency = 50; |
486 | faultTolerance = t.half; |
487 | } |
488 | break; |
489 | |
490 | default: |
491 | totalSpace = effectiveSpace = efficiency = 0; |
492 | faultTolerance = t.none; |
493 | } |
494 | |
495 | // Update results |
496 | document.getElementById('total-space').textContent = totalSpace.toLocaleString() + ' Gb'; |
497 | document.getElementById('effective-space').textContent = effectiveSpace.toLocaleString() + ' Gb'; |
498 | document.getElementById('efficiency').textContent = efficiency + ' %'; |
499 | document.getElementById('fault-tolerance').textContent = faultTolerance; |
500 | |
501 | // Update hint |
502 | updateHint(raidType); |
503 | |
504 | // Save state |
505 | saveStateToHash(); |
506 | } |
507 | |
508 | // Event listeners |
509 | function setupEventListeners() { |
510 | elements.diskCountInput.addEventListener('input', calculate); |
511 | elements.diskSizeInput.addEventListener('input', calculate); |
512 | elements.raidTypeSelect.addEventListener('change', calculate); |
513 | |
514 | document.getElementById('lang-en').addEventListener('click', () => { |
515 | currentLang = 'en'; |
516 | updateLanguage('en'); |
517 | saveStateToHash(); |
518 | }); |
519 | document.getElementById('lang-ru').addEventListener('click', () => { |
520 | currentLang = 'ru'; |
521 | updateLanguage('ru'); |
522 | saveStateToHash(); |
523 | }); |
524 | } |
525 | |
526 | // Initialize |
527 | function init() { |
528 | // Try to load state from hash |
529 | const stateLoaded = loadStateFromHash(); |
530 | |
531 | // Setup event listeners |
532 | setupEventListeners(); |
533 | |
534 | // Update UI with current or default values |
535 | updateLanguage(currentLang); |
536 | |
537 | // If no state was loaded, calculate with defaults |
538 | if (!stateLoaded) { |
539 | calculate(); |
540 | } |
541 | } |
542 | |
543 | // Start the application |
544 | init(); |
545 | </script> |
546 | </body> |
547 | </html> |