Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-129643 / 24.10 / Pool tint in new enclosure #10301

Merged
merged 3 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import { EnclosureStatus } from 'app/enums/enclosure-slot-status.enum';
import { DashboardEnclosure, DashboardEnclosureSlot } from 'app/interfaces/enclosure.interface';

export function addDisksToSlots(enclosures: DashboardEnclosure[], percentageToAdd: number): DashboardEnclosure[] {
const totalSlots = countSlots(enclosures);
let totalAddedDisks = -1;

return mapSlots(enclosures, ({ slot, index }) => {
if (index > totalSlots * percentageToAdd) {
return mapSlots(enclosures, ({ slot, enclosure, index }) => {
const slotsInEnclosure = countSlots(enclosure);
if (index > slotsInEnclosure * percentageToAdd) {
return slot;
}

return addDisk(slot, index);
totalAddedDisks = totalAddedDisks + 1;

return addDisk(slot, totalAddedDisks);
});
}

Expand Down Expand Up @@ -43,6 +46,5 @@ function generateDiskName(index: number): string {
index = Math.floor(index / base) - 1;
}

result = 'sd' + String.fromCharCode(offset + index) + result;
return result;
return 'sd' + String.fromCharCode(offset + index) + result;
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,73 @@
import { countSlotsBy, mapSlots } from 'app/core/testing/mock-enclosure/enclosure-templates/utils/slots.utils';
import { EnclosureDiskStatus } from 'app/enums/enclosure-slot-status.enum';
import { mapSlots } from 'app/core/testing/mock-enclosure/enclosure-templates/utils/slots.utils';
import { EnclosureDiskStatus, EnclosureElementType } from 'app/enums/enclosure-slot-status.enum';
import { VdevType } from 'app/enums/v-dev-type.enum';
import { DashboardEnclosure } from 'app/interfaces/enclosure.interface';
import { DashboardEnclosure, DashboardEnclosureSlot } from 'app/interfaces/enclosure.interface';

// TODO: Only creates single disk vdevs in the same pool. Improve.
// TODO: Messy.
export function addPoolsToDisks(enclosures: DashboardEnclosure[], percentageToAdd: number): DashboardEnclosure[] {
return mapSlots(enclosures, ({ slot, enclosure, index }) => {
const totalDisks = countSlotsBy([enclosure], ({ dev }) => Boolean(dev));
const disksPerVdev = 3;
const poolsToAdd = 40;

if (index >= totalDisks * percentageToAdd) {
return slot;
const slotsToUpdate: DashboardEnclosureSlot[] = [];
enclosures.forEach((enclosure) => {
const enclosureSlotsWithDisks = Object.values(enclosure.elements[EnclosureElementType.ArrayDeviceSlot])
.filter((slot) => Boolean(slot.dev));

const enclosureSlotsToUse = enclosureSlotsWithDisks.slice(
0,
Math.floor(enclosureSlotsWithDisks.length * percentageToAdd),
);

slotsToUpdate.push(...enclosureSlotsToUse);
});

const vdevs = groupDisksIntoVdevs(slotsToUpdate, disksPerVdev);

let i = 0;
return mapSlots(enclosures, ({ slot: originalSlot, enclosure }) => {
const updatedSlot = slotsToUpdate.find((slot) => slot.dev === originalSlot.dev);

if (!updatedSlot) {
return originalSlot;
}

const vdevIndex = Math.floor(i / disksPerVdev);
const vdev = vdevs[vdevIndex];
i = i + 1;

return {
...slot,
...originalSlot,
pool_info: {
pool_name: 'Test Pool',
pool_name: `pool-${vdevIndex % poolsToAdd + 1}`,
disk_status: EnclosureDiskStatus.Online,
disk_read_errors: 0,
disk_write_errors: 0,
disk_checksum_errors: 0,
vdev_name: 'stripe',
vdev_type: VdevType.Data,
vdev_disks: [{
enclosure_id: '5b0bd6d1a30714bf',
slot: slot.drive_bay_number,
dev: slot.dev,
}],
vdev_disks: vdev.map((disk) => ({
// TODO: Bug. Enclosure id should come from the disk.
enclosure_id: enclosure.id,
slot: disk.drive_bay_number,
dev: disk.dev,
})),
},
};
});
}

function groupDisksIntoVdevs(slots: DashboardEnclosureSlot[], disksPerVdev: number): DashboardEnclosureSlot[][] {
return slots.reduce((acc, slot, index) => {
const vdevIndex = Math.floor(index / disksPerVdev);
if (!acc[vdevIndex]) {
acc[vdevIndex] = [];
}

acc[vdevIndex].push(slot);
return acc;
}, [] as DashboardEnclosureSlot[][]);
}

export function randomizeDiskStatuses(enclosures: DashboardEnclosure[]): DashboardEnclosure[] {
const allStatuses = Object.values(EnclosureDiskStatus);
return mapSlots(enclosures, ({ slot, index }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { EnclosureElementType } from 'app/enums/enclosure-slot-status.enum';
import {
DashboardEnclosure,
DashboardEnclosureSlot,
DashboardEnclosureElements,
} from 'app/interfaces/enclosure.interface';
import { mapSlots, countSlots } from './slots.utils';

describe('mapSlots', () => {
it('should correctly map slots for each enclosure', () => {
const enclosures = [
{
elements: {
[EnclosureElementType.ArrayDeviceSlot]: {
1: { model: 'test1' } as DashboardEnclosureSlot,
2: { model: 'test2' } as DashboardEnclosureSlot,
},
} as DashboardEnclosureElements,
},
] as DashboardEnclosure[];

const result = mapSlots(enclosures, ({ slot }) => ({
...slot,
model: `mapped-${slot.model}`,
}));

expect(result).toEqual([
{
elements: {
[EnclosureElementType.ArrayDeviceSlot]: {
1: { model: 'mapped-test1' } as DashboardEnclosureSlot,
2: { model: 'mapped-test2' } as DashboardEnclosureSlot,
},
} as DashboardEnclosureElements,
},
]);
});
});

describe('countSlots', () => {
it('should return the correct number of slots', () => {
const enclosure = {
elements: {
[EnclosureElementType.ArrayDeviceSlot]: {
1: {} as DashboardEnclosureSlot,
2: {} as DashboardEnclosureSlot,
},
} as DashboardEnclosureElements,
} as DashboardEnclosure;
const result = countSlots(enclosure);
expect(result).toBe(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface SlotMappingFunction {

export function mapSlots(
enclosures: DashboardEnclosure[],
mappingFunction: ({ slot, enclosure, index }: SlotMappingFunction) => DashboardEnclosureSlot,
mappingFunction: ({ slot, index }: SlotMappingFunction) => DashboardEnclosureSlot,
): DashboardEnclosure[] {
return enclosures.map((enclosure) => {
let i = -1;
Expand All @@ -29,17 +29,6 @@ export function mapSlots(
});
}

export function countSlots(enclosures: DashboardEnclosure[]): number {
return enclosures.reduce((acc, enclosure) => {
return acc + Object.keys(enclosure.elements[EnclosureElementType.ArrayDeviceSlot]).length;
}, 0);
}

export function countSlotsBy(
enclosures: DashboardEnclosure[],
predicate: (slot: DashboardEnclosureSlot) => boolean,
): number {
return enclosures.reduce((acc, enclosure) => {
return acc + Object.values(enclosure.elements[EnclosureElementType.ArrayDeviceSlot]).filter(predicate).length;
}, 0);
export function countSlots(enclosure: DashboardEnclosure): number {
return Object.keys(enclosure.elements[EnclosureElementType.ArrayDeviceSlot]).length;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ export enum MockStorageScenario {

export const mockStorageScenarioLabels = new Map<MockStorageScenario, string>([
[MockStorageScenario.AllSlotsEmpty, 'All slots empty'],
[MockStorageScenario.FillSomeSlots, 'Add disks to some slots and add a pool to some disks.'],
[MockStorageScenario.FillAllSlots, 'Add disks to all slots and add a pool to all disks'],
[MockStorageScenario.FillSomeSlots, 'Add disks to some slots and add pools to some disks.'],
[MockStorageScenario.FillAllSlots, 'Add disks to all slots and add pools to all disks'],
[MockStorageScenario.DiskStatuses, 'Fill some slots and use all disk statuses'],
]);

/**
* @deprecated
*/
export enum MockDiskType {
'Hdd' = 'Hdd',
'Nvme' = 'Nvme',
Expand Down
4 changes: 2 additions & 2 deletions src/app/interfaces/enclosure-old.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DiskPowerLevel } from 'app/enums/disk-power-level.enum';
import { DiskStandby } from 'app/enums/disk-standby.enum';
import { EnclosureElement, EnclosureSlotMetadata, EnclosureVdev } from 'app/interfaces/enclosure.interface';
import { EnclosureElement, EnclosureSlotMetadata, EnclosureVdevDisk } from 'app/interfaces/enclosure.interface';

/**
* @deprecated
Expand Down Expand Up @@ -73,7 +73,7 @@ export interface EnclosureOldPool {
disk_status: string;
vdev_name: string;
vdev_type: string;
vdev_disks: EnclosureVdev[];
vdev_disks: EnclosureVdevDisk[];
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/app/interfaces/enclosure.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export interface DashboardEnclosureSlot {
pool_info: EnclosureSlotPoolInfo | null;
}

export interface EnclosureVdev {
export interface EnclosureVdevDisk {
enclosure_id: string;
slot: number;
dev: string;
Expand All @@ -102,7 +102,7 @@ export interface EnclosureSlotPoolInfo {
disk_checksum_errors?: number;
vdev_name: string;
vdev_type: VdevType;
vdev_disks: EnclosureVdev[];
vdev_disks: EnclosureVdevDisk[];
}

export type DashboardEnclosureElements = Overwrite<EnclosureElements, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
<div class="container">
@if (selectedSlot().dev) {
<h1>{{ selectedSlot().dev }}</h1>
}
@if (selectedSlot().pool_info) {
<span>
<span class="title">{{ 'Topology' | translate }}:</span>
<span class="value">{{ selectedSlot().pool_info.vdev_name }}</span>
@if (selectedSlot().dev) {
<h1>{{ selectedSlot().dev }}</h1>
}
@if (selectedSlot().pool_info) {
<div>
<span class="title">{{ 'Topology' | translate }}:</span>
<span class="value">{{ selectedSlot().pool_info.vdev_name }}</span>
</div>
}
@if (selectedSlot().pool_info) {
<div>
<span class="title">{{ 'Category' | translate }}:</span>
<span class="value">
{{ selectedSlot().pool_info.vdev_type | mapValue: vdevTypeLabels | translate }}
</span>
}
@if (selectedSlot().pool_info) {
<span>
<span class="title">{{ 'Category' | translate }}:</span>
<span class="value">
{{ selectedSlot().pool_info.vdev_type | mapValue: vdevTypeLabels | translate }}
</span>
</span>
}
<span>
<span class="title">{{ 'Slot' | translate }}:</span>
<span class="value">{{ selectedSlot().drive_bay_number }}</span>
</span>
</div>
}
<div>
<span class="title">{{ 'Slot' | translate }}:</span>
<span class="value">{{ selectedSlot().drive_bay_number }}</span>
</div>
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
.container {
align-items: center;
color: white;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
min-width: 105px;
:host {
text-align: center;
}

h1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<ix-disk-topology-description
[selectedSlot]="selectedSlot()"
></ix-disk-topology-description>
} @else {
<h2>{{ 'Pools' | translate }}</h2>
}
</div>

Expand All @@ -26,8 +28,15 @@

<div class="right-column">
@if (selectedSlot()) {
<ix-vdev-disks-list
<ix-vdev-disks-legend
[selectedSlot]="selectedSlot()"
></ix-vdev-disks-list>
[poolColor]="poolColor()"
(diskClick)="onVdevDiskClicked($event)"
></ix-vdev-disks-legend>
} @else {
<ix-pools-legend
[slots]="selectedEnclosureSlots()"
[poolColors]="poolColors()"
></ix-pools-legend>
}
</div>
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
@import 'scss-imports/cssvars';

:host {
box-sizing: border-box;
column-gap: 5px;
display: flex;
height: 100%;
padding: 0 5px;
width: 100%;

@media (max-width: $breakpoint-md) {
Expand All @@ -15,8 +17,14 @@

.left-column,
.right-column {
align-items: center;
display: flex;
flex: 1 1 25%;
flex-direction: column;
justify-content: center;
max-width: 200px;
min-height: 100%;
min-width: 105px;

@media (max-width: $breakpoint-md) {
max-width: 100%;
Expand Down
Loading
Loading