gistfile1.txt
· 25 KiB · Text
Raw
<!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> |