Last active 1746986465

Калькулятор RAID'ов большинства используемых типов ( https://blacktower.wintersky.ru/public/raid_calc.html )

Revision 508968d9ff6621a281f0bef11a8bd29181968eb6

gistfile1.txt Raw
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>