Skip to content

Commit

Permalink
NAS-128453: More support for new enclosure UI (#10043)
Browse files Browse the repository at this point in the history
  • Loading branch information
undsoft authored May 10, 2024
1 parent a4d8675 commit 39c95a5
Show file tree
Hide file tree
Showing 15 changed files with 3,498 additions and 96 deletions.
8 changes: 4 additions & 4 deletions src/app/core/testing/utils/mock-enclosure.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ export class MockEnclosureUtils {
private mockDiskGetUnused(): ApiCallResponse<'disk.get_unused'> {
return this.mockStorage.disks.filter((disk: Disk) => {
return !Object.keys(disk).includes('pool') || typeof disk.pool === 'undefined' || disk.pool === null;
}).map((disk: Disk) => {
}).map((_: Disk) => {
return {
...disk,
partitions: [{ path: '' }],
exported_zpool: '',
// ...disk,
// partitions: [{ path: '' }],
// exported_zpool: '',
} as UnusedDisk;
});
}
Expand Down
103 changes: 52 additions & 51 deletions src/app/core/testing/utils/mock-storage-generator.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import { DiskType } from 'app/enums/disk-type.enum';
import { PoolStatus } from 'app/enums/pool-status.enum';
import { TopologyItemType, VdevType } from 'app/enums/v-dev-type.enum';
import { TopologyItemStatus } from 'app/enums/vdev-status.enum';
import { EnclosureOld, EnclosureOldPool, EnclosureOldSlot } from 'app/interfaces/enclosure-old.interface';
import { EnclosureOld, EnclosureOldSlot } from 'app/interfaces/enclosure-old.interface';
import { Pool } from 'app/interfaces/pool.interface';
import {
Disk,
EnclosureIdAndSlot,
Disk, EnclosureIdAndSlot,
TopologyDisk,
TopologyItem,
TopologyItemStats,
Expand Down Expand Up @@ -775,38 +774,39 @@ export class MockStorageGenerator {
}

private syncEnclosurePoolInfo(enclosures: EnclosureOld[]): EnclosureOld[] {
this.poolState?.topology.data.forEach((item: TopologyItem) => {
if (item.type === TopologyItemType.Disk) {
const stripeDisk: Disk = this.disks.find((dev: Disk) => dev.name === item.disk);
const enclosure: EnclosureOld = enclosures.find((enclosureUi: EnclosureOld) => {
return enclosureUi.id === stripeDisk.enclosure.id;
});
enclosure.elements['Array Device Slot'][stripeDisk.enclosure?.slot].pool_info = null;
} else {
item.children.forEach((child) => {
const disk: Disk = this.disks.find((dev: Disk) => dev.name === child.disk);
if (disk) {
const enclosure: EnclosureOld = enclosures.find((enclosureUi: EnclosureOld) => {
return enclosureUi.id === disk.enclosure.id;
});
const poolInfo: EnclosureOldPool = {
pool_name: this.poolState.name,
disk_status: child.status,
vdev_name: item.name,
vdev_type: item.type,
vdev_disks: item.children.filter((unfilteredChild: TopologyDisk) => unfilteredChild.disk !== disk.name)
.map((sibling: TopologyDisk) => {
return {
enclosure_id: enclosure.id,
slot: disk.enclosure.slot,
dev: sibling.disk,
};
}),
};
enclosure.elements['Array Device Slot'][disk.enclosure?.slot].pool_info = poolInfo;
}
});
}
this.poolState?.topology.data.forEach((_: TopologyItem) => {
// TODO: Broken due to enclosure API changes
// if (item.type === TopologyItemType.Disk) {
// const stripeDisk: Disk = this.disks.find((dev: Disk) => dev.name === item.disk);
// const enclosure: EnclosureOld = enclosures.find((enclosureUi: EnclosureOld) => {
// return enclosureUi.id === stripeDisk.enclosure.id;
// });
// enclosure.elements['Array Device Slot'][stripeDisk.enclosure?.slot].pool_info = null;
// } else {
// item.children.forEach((child) => {
// const disk: Disk = this.disks.find((dev: Disk) => dev.name === child.disk);
// if (disk) {
// const enclosure: EnclosureOld = enclosures.find((enclosureUi: EnclosureOld) => {
// return enclosureUi.id === disk.enclosure.id;
// });
// const poolInfo: EnclosureOldPool = {
// pool_name: this.poolState.name,
// disk_status: child.status,
// vdev_name: item.name,
// vdev_type: item.type,
// vdev_disks: item.children.filter((unfilteredChild: TopologyDisk) => unfilteredChild.disk !== disk.name)
// .map((sibling: TopologyDisk) => {
// return {
// enclosure_id: enclosure.id,
// slot: disk.enclosure.slot,
// dev: sibling.disk,
// };
// }),
// };
// enclosure.elements['Array Device Slot'][disk.enclosure?.slot].pool_info = poolInfo;
// }
// });
// }
});
return enclosures;
}
Expand Down Expand Up @@ -875,12 +875,12 @@ export class MockStorageGenerator {

// Update the disk data
const updatedDisk: Disk = { ...disk }; // Object.assign({}, disk)
const enclosureAndSlot: EnclosureIdAndSlot = {
id: mockEnclosures[enclosureNumber].data.id,
slot: slotNumber,
number: 0, // TODO: Remove when Disk interface is updated
};
updatedDisk.enclosure = enclosureAndSlot;
// const enclosureAndSlot: EnclosureIdAndSlot = {
// id: mockEnclosures[enclosureNumber].data.id,
// slot: slotNumber,
// number: 0, // TODO: Remove when Disk interface is updated
// };
// updatedDisk.enclosure = enclosureAndSlot;
updatedDisks.push(updatedDisk);
}

Expand All @@ -902,11 +902,11 @@ export class MockStorageGenerator {
private existingDispersal(mockEnclosures: MockEnclosure[], disks: Disk[]): DispersedData {
// When working with disks with enclosure data already assigned
// eg. UI is pointed at a product that already has enclosure support
disks.forEach((disk: Disk) => {
if (!disk.enclosure) return;

const mockEnclosure = mockEnclosures[disk.enclosure.number];
mockEnclosure.addDiskToSlot(disk.name, disk.enclosure.slot);
disks.forEach((_: Disk) => {
// if (!disk.enclosure) return;
//
// const mockEnclosure = mockEnclosures[disk.enclosure.number];
// mockEnclosure.addDiskToSlot(disk.name, disk.enclosure.slot);
});

return {
Expand Down Expand Up @@ -972,11 +972,12 @@ export class MockStorageGenerator {
};
}

private removeDiskFromEnclosure(disk: Disk): this {
const mockEnclosureIndex: number = this.mockEnclosures.findIndex((mockEnclosure: MockEnclosure) => {
return mockEnclosure.data.number === disk.enclosure.number;
});
this.mockEnclosures[mockEnclosureIndex].removeDiskFromSlot(disk.name, disk.enclosure.slot);
private removeDiskFromEnclosure(_: Disk): this {
// TODO: Enclosure mapping is broken because of API changes.
// const mockEnclosureIndex: number = this.mockEnclosures.findIndex((mockEnclosure: MockEnclosure) => {
// return mockEnclosure.data.number === disk.enclosure.number;
// });
// this.mockEnclosures[mockEnclosureIndex].removeDiskFromSlot(disk.name, disk.enclosure.slot);

return this;
}
Expand Down
27 changes: 19 additions & 8 deletions src/app/enums/enclosure-slot-status.enum.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import { TopologyItemStatus } from './vdev-status.enum';

export enum EnclosureSlotStatus {
Clear = 'CLEAR',
Fault = 'FAULT',
Identify = 'IDENTIFY',
}

export enum EnclosureSlotDiskStatus {
Available = 'AVAILABLE',
Fault = 'FAULT',
export enum EnclosureStatus {
Ok = 'OK',
Invop = 'INVOP',
Info = 'INFO',
NonCrit = 'NON-CRIT',
Crit = 'CRIT',
Unrecov = 'UNRECOV',
}

export enum EnclosureDiskStatus {
Offline = 'OFFLINE',
Removed = 'REMOVED',
Faulted = 'FAULTED',
Split = 'SPLIT',
Unavail = 'UNAVAIL',
Degraded = 'DEGRADED',
Online = 'ONLINE',
Unknown = 'UNKNOWN',
}

// TODO: Find out what all element name possibilities are to complete the enum
export enum EnclosureElementName {
export enum EnclosureElementType {
ArrayDeviceSlot = 'Array Device Slot',
Enclosure = 'Enclosure',
PowerSupply = 'Power Supply',
Expand All @@ -25,5 +38,3 @@ export enum EnclosureElementName {
CurrentSensor = 'Current Sensor',
DoorLock = 'Door Lock',
}

export type EnclosureSlotTopologyStatus = TopologyItemStatus | EnclosureSlotDiskStatus;
30 changes: 15 additions & 15 deletions src/app/interfaces/enclosure.interface.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Overwrite } from 'utility-types';
import { DiskType } from 'app/enums/disk-type.enum';
import { EnclosureStatus, EnclosureDiskStatus, EnclosureElementType } from 'app/enums/enclosure-slot-status.enum';
import { VdevType } from 'app/enums/v-dev-type.enum';

export interface Enclosure {
name: string;
model: string;
controller: boolean;
dmi: string;
status: string;
status: EnclosureStatus[];
id: string;
vendor: string;
product: string;
Expand Down Expand Up @@ -41,35 +43,33 @@ export interface EnclosureSlotMetadata {
slot: number;
}

export interface EnclosureElements {
'Array Device Slot': Record<number, EnclosureSlot>;
'SAS Expander'?: Record<number, EnclosureElement>;
'Enclosure'?: Record<number, EnclosureElement>;
'Temperature'?: Record<number, EnclosureElement>;
'Voltage Sensor'?: Record<number, EnclosureElement>;
'Cooling'?: Record<number, EnclosureElement>;
}
export type EnclosureElements = {
[key in EnclosureElementType]?: Record<number, EnclosureElement>;
} & {
[EnclosureElementType.ArrayDeviceSlot]: Record<number, EnclosureSlot>;
};

export interface EnclosureElement {
descriptor: string;
status: string;
value: string;
value_raw: number;
value?: string;
value_raw?: number;
}

export type DashboardEnclosure = Overwrite<Enclosure, {
elements: DashboardEnclosureElements;
}>;

export interface DashboardEnclosureSlot {
drive_bay_number: number;
descriptor: string;
status: string; // TODO: Enum?
status: string;
dev: string;
supports_identify_light: boolean;
size: number;
model: string;
serial: string;
type: string; // TODO: Enum HDD,
type: DiskType;
rotationrate: number;
pool_info: EnclosureSlotPoolInfo | null;
}
Expand All @@ -82,7 +82,7 @@ export interface EnclosureVdev {

export interface EnclosureSlotPoolInfo {
pool_name: string;
disk_status: string; // TODO: Enum: ONLINE,
disk_status: EnclosureDiskStatus;
disk_read_errors: number;
disk_write_errors: number;
disk_checksum_errors: number;
Expand All @@ -92,5 +92,5 @@ export interface EnclosureSlotPoolInfo {
}

export type DashboardEnclosureElements = Overwrite<EnclosureElements, {
'Array Device Slot': Record<number, DashboardEnclosureSlot>;
[EnclosureElementType.ArrayDeviceSlot]: Record<number, DashboardEnclosureSlot>;
}>;
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,8 @@ export class WidgetPoolComponent extends WidgetComponent implements AfterViewIni
if ((!currentName || currentName === 'unknown') && disks.length === 0) {
this.currentDiskDetails = null;
} else if (currentName && disks.length > 0 && currentName === disks[0].name) {
delete disks[0].enclosure;
delete disks[0].name;
delete disks[0].devname;
delete disks[0].multipath_name;
delete disks[0].multipath_member;
delete disks[0].zfs_guid;
this.currentDiskDetails = disks[0];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export class TopologyCardComponent implements OnInit, OnChanges {
return this.poolState?.status === PoolStatus.Offline;
}

// TODO: Unclear why this conversion is needed.
dashboardDiskToDisk(dashDisk: StorageDashboardDisk): Disk {
const output: EmptyDiskObject | Disk = {};
const keys: string[] = Object.keys(dashDisk);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
:host {
cursor: pointer;

// TODO: Check if colors need to be theme specific
.tray-background {
fill: #2d2d2d;
}

&:hover {
.tray-background {
fill: #ff3;
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {
ChangeDetectionStrategy, Component, computed, input, output, signal,
ChangeDetectionStrategy, Component, computed, input, output,
} from '@angular/core';
import { DashboardEnclosureSlot } from 'app/interfaces/enclosure.interface';

@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: '[ixDriveTray]',
styleUrls: ['./drive-tray.component.scss'],
templateUrl: './drive-tray.component.svg',
changeDetection: ChangeDetectionStrategy.OnPush,
})
Expand All @@ -16,7 +17,4 @@ export class DriveTrayComponent {
readonly traySelected = output();

readonly isEmpty = computed(() => !this.slot().pool_info);

// TODO: Try with CSS
readonly highlight = signal(false);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { KeyValue } from '@angular/common';
import {
ChangeDetectionStrategy, Component, computed, input, TrackByFunction,
} from '@angular/core';
import { EnclosureElementType } from 'app/enums/enclosure-slot-status.enum';
import { DashboardEnclosure, DashboardEnclosureSlot } from 'app/interfaces/enclosure.interface';
import { EnclosureComponent } from 'app/pages/system/enclosure/interfaces/enclosure-component.interface';
import { EnclosureStore } from 'app/pages/system/enclosure/services/enclosure.store';
Expand All @@ -17,7 +18,7 @@ export class M50EnclosureComponent implements EnclosureComponent {

readonly trackByNumber: TrackByFunction<KeyValue<string, DashboardEnclosureSlot>> = (_, slot) => slot.key;

readonly slots = computed(() => this.enclosure().elements['Array Device Slot']);
readonly slots = computed(() => this.enclosure().elements[EnclosureElementType.ArrayDeviceSlot]);

getDriveTrayTransformation(slotNumber: string): string {
const number = Number(slotNumber);
Expand Down
3 changes: 3 additions & 0 deletions src/app/pages/system/enclosure/tests/mocks/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
For now just mock directly in websocket service.

TODO: Move to CLI mocking utility.
Loading

0 comments on commit 39c95a5

Please sign in to comment.