Skip to content

Commit

Permalink
NAS-132779 / 25.04 / Fibre channel port card (#11141)
Browse files Browse the repository at this point in the history
* NAS-132779: Fibre channel port card

* NAS-132779: PR update

* NAS-132779: PR update

* NAS-132779: PR Update
  • Loading branch information
AlexKarpov98 authored Dec 5, 2024
1 parent 8d8fa56 commit 8570a11
Show file tree
Hide file tree
Showing 95 changed files with 488 additions and 3 deletions.
11 changes: 10 additions & 1 deletion src/app/interfaces/fibre-channel.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ export interface FibreChannelPort {
port: string;
wwpn: string | null;
wwpn_b: string | null;
target: unknown; // TODO: Probably IscsiTarget
target: FibreChannelTarget;
}

export interface FibreChannelTarget {
id: number;
iscsi_target_name: string;
iscsi_target_alias: string | null;
iscsi_target_mode: string;
iscsi_target_auth_networks: string[];
iscsi_target_rel_tgt_id: number;
}

export interface FibreChannelPortUpdate {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<mat-card class="card">
<mat-card-header>
<h3 mat-card-title>
{{ 'Fibre Channel Port' | translate }}
</h3>
</mat-card-header>
<mat-card-content>
<p>{{ 'Name' | translate }}: {{ port().id }}</p>
<p>{{ 'Controller A WWPN' | translate }}: {{ port().wwpn }}</p>
<p>{{ 'Controller B WWPN' | translate }}: {{ port().wwpn_b }}</p>
</mat-card-content>
</mat-card>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { TranslateModule } from '@ngx-translate/core';
import { FibreChannelPort } from 'app/interfaces/fibre-channel.interface';
import { FibreChannelPortCardComponent } from './fibre-channel-port-card.component';

describe('FibreChannelPortCardComponent', () => {
let spectator: Spectator<FibreChannelPortCardComponent>;
const createComponent = createComponentFactory({
component: FibreChannelPortCardComponent,
imports: [TranslateModule.forRoot()],
});

beforeEach(() => {
spectator = createComponent({
props: {
port: {
id: 'Port-1',
wwpn: '10:00:00:00:c9:20:00:00',
wwpn_b: '10:00:00:00:c9:20:00:01',
} as unknown as FibreChannelPort,
},
});
});

it('renders Fibre Channel Port title', () => {
const title = spectator.query('h3[mat-card-title]');
expect(title).toHaveText('Fibre Channel Port');
});

it('displays port details correctly', () => {
const content = spectator.queryAll('mat-card-content p');
expect(content).toHaveLength(3);
expect(content[0]).toHaveText('Name: Port-1');
expect(content[1]).toHaveText('Controller A WWPN: 10:00:00:00:c9:20:00:00');
expect(content[2]).toHaveText('Controller B WWPN: 10:00:00:00:c9:20:00:01');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import {
MatCard, MatCardContent, MatCardHeader, MatCardTitle,
} from '@angular/material/card';
import { TranslateModule } from '@ngx-translate/core';
import { FibreChannelPort } from 'app/interfaces/fibre-channel.interface';

@Component({
selector: 'ix-fibre-channel-port-card',
styleUrls: ['./fibre-channel-port-card.component.scss'],
templateUrl: './fibre-channel-port-card.component.html',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
MatCard,
MatCardHeader,
MatCardTitle,
TranslateModule,
MatCardContent,
],
})
export class FibreChannelPortCardComponent {
readonly port = input.required<FibreChannelPort>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
}
}

@if (hasFibreCards()) {
@if (targetPort()) {
<ix-fibre-channel-port-card [port]="targetPort()"></ix-fibre-channel-port-card>
}
}

<ix-associated-extents-card [target]="target()"></ix-associated-extents-card>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { MockComponents } from 'ng-mocks';
import { of } from 'rxjs';
import { mockApi, mockCall } from 'app/core/testing/utils/mock-api.utils';
import { IscsiTargetMode } from 'app/enums/iscsi.enum';
import { FibreChannelPort } from 'app/interfaces/fibre-channel.interface';
import { IscsiTarget } from 'app/interfaces/iscsi.interface';
import {
AuthorizedNetworksCardComponent,
} from 'app/pages/sharing/iscsi/target/all-targets/target-details/authorized-networks-card/authorized-networks-card.component';
import {
FibreChannelPortCardComponent,
} from 'app/pages/sharing/iscsi/target/all-targets/target-details/fibre-channel-port-card/fibre-channel-port-card.component';
import { ApiService } from 'app/services/websocket/api.service';
import { TargetDetailsComponent } from './target-details.component';

describe('TargetDetailsComponent', () => {
let spectator: Spectator<TargetDetailsComponent>;
let mockApiService: jest.Mocked<ApiService>;

const mockPort = {
id: 'Port-1',
wwpn: '10:00:00:00:c9:20:00:00',
wwpn_b: '10:00:00:00:c9:20:00:01',
} as unknown as FibreChannelPort;

const createComponent = createComponentFactory({
component: TargetDetailsComponent,
declarations: [
MockComponents(AuthorizedNetworksCardComponent, FibreChannelPortCardComponent),
],
providers: [
mockApi([
mockCall('fcport.query', [mockPort]),
mockCall('iscsi.extent.query', []),
mockCall('iscsi.targetextent.query', []),
]),
],
});

beforeEach(() => {
spectator = createComponent({
props: {
target: {
id: 1,
mode: IscsiTargetMode.Both,
auth_networks: ['192.168.1.0/24', '10.0.0.0/24'],
} as IscsiTarget,
},
});

mockApiService = spectator.inject(ApiService);
});

it('renders AuthorizedNetworksCardComponent if target has authorized networks', () => {
expect(spectator.query(AuthorizedNetworksCardComponent)).toExist();
expect(spectator.query(AuthorizedNetworksCardComponent)?.target).toEqual({
id: 1,
mode: IscsiTargetMode.Both,
auth_networks: ['192.168.1.0/24', '10.0.0.0/24'],
});
});

it('renders FibreChannelPortCardComponent if targetPort is set', () => {
spectator.detectChanges();
expect(spectator.query(FibreChannelPortCardComponent)).toExist();
expect(spectator.query(FibreChannelPortCardComponent)?.port).toEqual(mockPort);
});

it('does not render FibreChannelPortCardComponent if no targetPort is available', () => {
spectator.component.targetPort.set(null);
spectator.detectChanges();

expect(spectator.query(FibreChannelPortCardComponent)).toBeNull();
});

it('calls API to fetch Fibre Channel ports when target ID changes', () => {
mockApiService.call.mockReturnValue(of([]));
spectator.setInput({
target: {
id: 2,
mode: 'FC',
auth_networks: [],
} as IscsiTarget,
});

spectator.detectChanges();

expect(mockApiService.call).toHaveBeenCalledWith('fcport.query', [[['target.id', '=', 2]]]);
});
});
Original file line number Diff line number Diff line change
@@ -1,25 +1,67 @@
import {
ChangeDetectionStrategy, Component, computed, input,
ChangeDetectionStrategy, Component, computed, effect, input,
signal,
} from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { take } from 'rxjs';
import { IscsiTargetMode } from 'app/enums/iscsi.enum';
import { FibreChannelPort } from 'app/interfaces/fibre-channel.interface';
import { IscsiTarget } from 'app/interfaces/iscsi.interface';
import { AssociatedExtentsCardComponent } from 'app/pages/sharing/iscsi/target/all-targets/target-details/associated-extents-card/associated-extents-card.component';
import {
AuthorizedNetworksCardComponent,
} from 'app/pages/sharing/iscsi/target/all-targets/target-details/authorized-networks-card/authorized-networks-card.component';
import { FibreChannelPortCardComponent } from 'app/pages/sharing/iscsi/target/all-targets/target-details/fibre-channel-port-card/fibre-channel-port-card.component';
import { ApiService } from 'app/services/websocket/api.service';

@UntilDestroy()
@Component({
selector: 'ix-target-details',
templateUrl: './target-details.component.html',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AuthorizedNetworksCardComponent,
FibreChannelPortCardComponent,
AssociatedExtentsCardComponent,
],
})
export class TargetDetailsComponent {
readonly target = input.required<IscsiTarget>();

protected hasIscsiCards = computed(() => [IscsiTargetMode.Iscsi, IscsiTargetMode.Both].includes(this.target().mode));
targetPort = signal<FibreChannelPort>(null);

protected hasIscsiCards = computed(() => [
IscsiTargetMode.Iscsi,
IscsiTargetMode.Both,
].includes(this.target().mode));

protected hasFibreCards = computed(() => [
IscsiTargetMode.Fc,
IscsiTargetMode.Both,
].includes(this.target().mode));

constructor(
private api: ApiService,
) {
effect(() => {
const targetId = this.target().id;
this.targetPort.set(null);

if (targetId) {
this.getPortByTargetId(targetId);
}
}, { allowSignalWrites: true });
}

private getPortByTargetId(id: number): void {
this.api.call('fcport.query', [[['target.id', '=', id]]])
.pipe(
take(1),
untilDestroyed(this),
)
.subscribe((ports) => {
this.targetPort.set(ports[0] || null);
});
}
}
3 changes: 3 additions & 0 deletions src/assets/i18n/af.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/az.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/be.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/i18n/bn.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,8 @@
"Contract Type": "",
"Control": "",
"Controller": "",
"Controller A WWPN": "",
"Controller B WWPN": "",
"Controller Type": "",
"Controls": "",
"Controls whether <a href=\"https://www.truenas.com/docs/scale/scaleuireference/dataprotection/smarttestsscreensscale/\" target=\"_blank\">SMART monitoring and scheduled SMART tests</a> are enabled.": "",
Expand Down Expand Up @@ -1887,6 +1889,7 @@
"Fetch DataStores": "",
"Fetching Encryption Summary": "",
"Fetching Encryption Summary for {dataset}": "",
"Fibre Channel Port": "",
"Fibre Channel Ports": "",
"Field is required": "",
"File": "",
Expand Down
Loading

0 comments on commit 8570a11

Please sign in to comment.