Skip to content

Commit 3ed3a61

Browse files
committed
Merge branch 'up/feat/raid-calculator' into chore/all-my-stuffs
2 parents bf33214 + 89e8184 commit 3ed3a61

File tree

6 files changed

+315
-2
lines changed

6 files changed

+315
-2
lines changed

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ import { tool as textToUnicodeNames } from './text-to-unicode-names';
106106
import { tool as torrentToMagnet } from './torrent-to-magnet';
107107
import { tool as ttlCalculator } from './ttl-calculator';
108108
import { tool as unicodeFormatter } from './unicode-formatter';
109+
import { tool as raidCalculator } from './raid-calculator';
109110

110111
import { tool as asciiTextDrawer } from './ascii-text-drawer';
111112

@@ -447,6 +448,7 @@ export const toolsByCategory: ToolCategory[] = [
447448
ipRangeToCidr,
448449
option43Generator,
449450
portNumbers,
451+
raidCalculator,
450452
],
451453
},
452454
{

src/tools/raid-calculator/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Database } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'RAID Calculator',
6+
path: '/raid-calculator',
7+
description: 'Calculate storage capacity, fault tolerance and space efficiency of an array based on the number of disks, size, and RAID type',
8+
keywords: ['raid', 'calculator'],
9+
component: () => import('./raid-calculator.vue'),
10+
icon: Database,
11+
createdAt: new Date('2024-07-27'),
12+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test.describe('Tool - RAID Calculator', () => {
4+
test.beforeEach(async ({ page }) => {
5+
await page.goto('/raid-calculator');
6+
});
7+
8+
test('Has correct title', async ({ page }) => {
9+
await expect(page).toHaveTitle('RAID Calculator - IT Tools');
10+
});
11+
12+
test('', async ({ page }) => {
13+
14+
});
15+
});
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
export { raidCalculations };
2+
3+
interface RaidType {
4+
about: string
5+
requirements: string
6+
validate(num: number, size: number, stripeSize: number): boolean
7+
capacity(num: number, size: number, stripeSize: number, unit: number): number
8+
efficiency(num: number, stripeSize: number): number
9+
fault(num: number, size: number, unit: number): string
10+
}
11+
12+
const raidCalculations: { [key: string]: RaidType } = {
13+
raid_0: {
14+
about: 'RAID 0 splits data evenly across 2 or more disks without redunancy or fault tolerance creating one large storage space.',
15+
requirements: 'RAID 0 requires at least 1 disk',
16+
validate(num: number, size: number, stripeSize: number) {
17+
return num > 1;
18+
},
19+
capacity(num: number, size: number, stripeSize: number, unit: number) {
20+
// total disks * size
21+
return (num * size) * unit;
22+
},
23+
efficiency(num: number, stripeSize: number) {
24+
// uses 100% of space
25+
return 100;
26+
},
27+
fault(num: number, size: number, unit: number) {
28+
return 'None';
29+
},
30+
},
31+
raid_1: {
32+
about: 'RAID 1 consists of an exact copy of the data (mirror) across two or more disks. The array will operate as long as at least one drive is operational.',
33+
requirements: 'RAID 1 requires at least 1 disk',
34+
validate(num: number, size: number, stripeSize: number) {
35+
return num > 1;
36+
},
37+
capacity(num: number, size: number, stripeSize: number, unit: number) {
38+
// total size is size of a single drive
39+
return size * unit;
40+
},
41+
efficiency(num: number, stripeSize: number) {
42+
// 1/N
43+
return (1 / num) * 100;
44+
},
45+
fault(num: number, size: number, unit: number) {
46+
// FT = total - 1
47+
return `${num - 1} drive failures`;
48+
},
49+
},
50+
raid_5: {
51+
about: 'RAID 5 uses block level striping with parity. This allows for fault tolerance with a storage reduction equal to one drive for the parity information.',
52+
requirements: 'RAID 5 requires at least 3 disks',
53+
validate(num: number, size: number, stripeSize: number) {
54+
return num >= 3;
55+
},
56+
capacity(num: number, size: number, stripeSize: number, unit: number) {
57+
// (N-1) * S (one drive for parity)
58+
return ((num - 1) * size) * unit;
59+
},
60+
efficiency(num: number, stripeSize: number) {
61+
// 1 - (1/N)
62+
return (1 - (1 / num)) * 100;
63+
},
64+
fault(num: number, size: number, unit: number) {
65+
// always 1 failure
66+
return '1 drive failure';
67+
},
68+
},
69+
raid_6: {
70+
about: 'RAID 6 is similiar to RAID 5 but with an additional parity block. This allows for an additional disk failure at the cost of storage reduction equal to two drives.',
71+
requirements: 'RAID 6 requires at least 4 disks',
72+
validate(num: number, size: number, stripeSize: number) {
73+
return num >= 4;
74+
},
75+
capacity(num: number, size: number, stripeSize: number, unit: number) {
76+
// (N-2) * S (2 parity)
77+
return ((num - 2) * size) * unit;
78+
},
79+
efficiency(num: number, stripeSize: number) {
80+
// 1 - (2/N)
81+
return (1 - (2 / num)) * 100;
82+
},
83+
fault(num: number, size: number, unit: number) {
84+
// always 2 drive failures
85+
return '2 drive failures';
86+
},
87+
},
88+
raid_10: {
89+
about: 'RAID 10 is a stripe of mirrors (RAID 1 + RAID 0). Each set of drives is mirrored and striped together so that each drive in the set is fault tolerant within the group.',
90+
requirements: 'RAID 10 requires an even number of at least 4 disks',
91+
validate(num: number, size: number, stripeSize: number) {
92+
return num >= 4 && num % 2 === 0;
93+
},
94+
capacity(num: number, size: number, stripeSize: number, unit: number) {
95+
// Total disks (stripe)/2 (mirror)
96+
return ((num * size) / 2) * unit;
97+
},
98+
efficiency(num: number, stripeSize: number) {
99+
// 1/2 (1/strips per stripe, 2 in this case)
100+
return 50;
101+
},
102+
fault(num: number, size: number, unit: number) {
103+
// one per mirror
104+
return '1 drive failure per mirrored set';
105+
},
106+
},
107+
raid_50: {
108+
about: 'RAID 50 stripes multiple RAID 5 arrays together (RAID 5 + RAID 0). Each RAID 5 set can sustain a single drive failure.',
109+
requirements: 'RAID 50 requires at least 6 disks with 3 minimum per stripe. Stripes must contain an equal number of disks.',
110+
validate(num: number, size: number, stripeSize: number) {
111+
return num >= 6 && stripeSize >= 3 && num % stripeSize === 0;
112+
},
113+
capacity(num: number, size: number, stripeSize: number, unit: number) {
114+
// RAID 5 per stripe
115+
const perStripe = ((stripeSize - 1) * size) * unit;
116+
117+
// sum each stripe
118+
return perStripe * (num / stripeSize);
119+
},
120+
efficiency(num: number, stripeSize: number) {
121+
// 1 - (1 / strips per stripe)
122+
return (1 - (1 / stripeSize)) * 100;
123+
},
124+
fault(num: number, size: number, unit: number) {
125+
// one per set
126+
return '1 drive failure per RAID 5 set';
127+
},
128+
},
129+
raid_60: {
130+
about: 'RAID 60 stripes multiple RAID 6 arrays together (RAID 6 + RAID 0). Each RAID 6 set can sustain a two drive failures.',
131+
requirements: 'RAID 60 requires at least 8 disks with 4 minimum per stripe. Stripes must contain an equal number of disks.',
132+
validate(num: number, size: number, stripeSize: number) {
133+
return num >= 8 && stripeSize >= 4 && num % stripeSize === 0;
134+
},
135+
capacity(num: number, size: number, stripeSize: number, unit: number) {
136+
// RAID 6 per stripe
137+
const perStripe = ((stripeSize - 2) * size) * unit;
138+
139+
// sum each stripe
140+
return perStripe * (num / stripeSize);
141+
},
142+
efficiency(num: number, stripeSize: number) {
143+
// 1 - (2 / strips per stripe)
144+
return (1 - (2 / stripeSize)) * 100;
145+
},
146+
fault(num: number, size: number, unit: number) {
147+
// 2 per set
148+
return '2 drive failures per RAID 6 set';
149+
},
150+
},
151+
};
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<script setup lang="ts">
2+
import { raidCalculations } from './raid-calculator.service';
3+
import { UNIT_BASE, formatBytes } from '@/utils/convert';
4+
5+
const diskTotal = ref(2);
6+
const diskSize = ref(100);
7+
const diskUnit = ref(10 ** 9);
8+
const diskPerStripe = ref(3);
9+
const raidType = ref('raid_0');
10+
const raidInfo = computed(() => raidCalculations[raidType.value].about);
11+
const raidRequirements = computed(() => raidCalculations[raidType.value].requirements);
12+
const inputsValid = computed(() => validateSetup());
13+
14+
const totalStripes = computed(() => {
15+
if (inputsValid.value) {
16+
return `${diskTotal.value / diskPerStripe.value} stripes total`;
17+
}
18+
else {
19+
return '';
20+
}
21+
});
22+
23+
const calculatedCapacity = computed(() => {
24+
return formatBytes(raidCalculations[raidType.value].capacity(diskTotal.value, diskSize.value, diskPerStripe.value, diskUnit.value), 2, UNIT_BASE.BASE_10);
25+
});
26+
27+
const calculatedFaultTolerance = computed(() => {
28+
return raidCalculations[raidType.value].fault(diskTotal.value, diskSize.value, diskUnit.value);
29+
});
30+
31+
const calculatedSpaceEfficiency = computed(() => {
32+
return raidCalculations[raidType.value].efficiency(diskTotal.value, diskPerStripe.value).toFixed(1);
33+
});
34+
35+
function validateSetup() {
36+
// validate the selected RAID type against parameters
37+
return raidCalculations[raidType.value].validate(diskTotal.value, diskSize.value, diskPerStripe.value);
38+
}
39+
</script>
40+
41+
<template>
42+
<div>
43+
<c-card>
44+
<n-form-item label="Number of disks" label-placement="left" label-width="150" mb-2>
45+
<n-input-number v-model:value="diskTotal" max="10000" min="2" placeholder="Number of disks (ex: 2)" w-full />
46+
</n-form-item>
47+
48+
<n-form-item label="Disk size" label-placement="left" label-width="150" mb-2>
49+
<n-input-number v-model:value="diskSize" max="10000" min="1" placeholder="Disk size (ex: 100)" w-full />
50+
<div flex items-baseline gap-2>
51+
<c-select
52+
v-model:value="diskUnit"
53+
min-w-130px
54+
ml-1
55+
:options="[
56+
{ label: 'MB', value: 10 ** 6 },
57+
{ label: 'GB', value: 10 ** 9 },
58+
{ label: 'TB', value: 10 ** 12 },
59+
{ label: 'PB', value: 10 ** 15 },
60+
]"
61+
/>
62+
</div>
63+
</n-form-item>
64+
<n-form-item v-if="['raid_50', 'raid_60'].includes(raidType)" label="Disks per stripe" label-placement="left" label-width="150" mb-2>
65+
<n-input-number v-model:value="diskPerStripe" max="10000" min="2" placeholder="Number of disks per stripe (ex: 3)" w-full />
66+
<n-input v-model:value="totalStripes" placeholder="" ml-1 w-full readonly />
67+
</n-form-item>
68+
<n-form-item label="RAID Type" label-placement="left" label-width="150" mb-2>
69+
<c-select
70+
v-model:value="raidType"
71+
w-full
72+
:options="[
73+
{ label: 'RAID 0 (stripe)', value: 'raid_0' },
74+
{ label: 'RAID 1 (mirror)', value: 'raid_1' },
75+
{ label: 'RAID 5 (parity)', value: 'raid_5' },
76+
{ label: 'RAID 6 (double parity)', value: 'raid_6' },
77+
{ label: 'RAID 10 (mirror + stripe)', value: 'raid_10' },
78+
{ label: 'RAID 50 (parity + stripe)', value: 'raid_50' },
79+
{ label: 'RAID 60 (double parity + stripe)', value: 'raid_60' },
80+
]"
81+
/>
82+
</n-form-item>
83+
<p v-if="!inputsValid" class="raidError">
84+
{{ raidRequirements }}
85+
</p>
86+
<p>
87+
{{ raidInfo }}<br /><br />
88+
For more information on RAID types, see <a href="https://en.wikipedia.org/wiki/Standard_RAID_levels" target="_blank" rel="noopener">Wikipedia</a>.
89+
</p>
90+
</c-card>
91+
<c-card title="Results">
92+
<n-table v-if="inputsValid">
93+
<tbody>
94+
<tr>
95+
<td font-bold width="30%">
96+
Capacity
97+
</td>
98+
<td>
99+
{{ calculatedCapacity }}
100+
</td>
101+
</tr>
102+
<tr>
103+
<td font-bold width="30%">
104+
Fault Tolerance
105+
</td>
106+
<td>
107+
{{ calculatedFaultTolerance }}
108+
</td>
109+
</tr>
110+
<tr>
111+
<td font-bold width="30%">
112+
Space Efficiency
113+
</td>
114+
<td>
115+
{{ calculatedSpaceEfficiency }}%
116+
</td>
117+
</tr>
118+
</tbody>
119+
</n-table>
120+
</c-card>
121+
</div>
122+
</template>
123+
124+
<style lang="less" scoped>
125+
.raidError {
126+
color: rgb(208, 48, 80)
127+
}
128+
</style>

src/utils/convert.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
export function formatBytes(bytes: number, decimals = 2) {
1+
export enum UNIT_BASE {
2+
BASE_2 = 1024,
3+
BASE_10 = 1000,
4+
}
5+
6+
export function formatBytes(bytes: number, decimals = 2, base: UNIT_BASE = UNIT_BASE.BASE_2) {
27
if (bytes === 0) {
38
return '0 Bytes';
49
}
510

6-
const k = 1024;
11+
const k = base;
712
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
813
const i = Math.floor(Math.log(bytes) / Math.log(k));
914

0 commit comments

Comments
 (0)