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-133354 / 25.04 / QA iSCSI pages #11300

Merged
merged 1 commit into from
Jan 9, 2025
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 @@ -7,6 +7,7 @@ import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectat
import { of } from 'rxjs';
import { MockApiService } from 'app/core/testing/classes/mock-api.service';
import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils';
import { mockAuth } from 'app/core/testing/utils/mock-auth.utils';
import { DirectoryServiceState } from 'app/enums/directory-service-state.enum';
import { ApiEvent } from 'app/interfaces/api-message.interface';
import { DirectoryServicesState } from 'app/interfaces/directory-services-state.interface';
Expand All @@ -25,6 +26,7 @@ describe('DirectoryServicesIndicatorComponent', () => {
const createComponent = createComponentFactory({
component: DirectoryServicesIndicatorComponent,
providers: [
mockAuth(),
mockApi([
mockCall('directoryservices.get_state', {
activedirectory: DirectoryServiceState.Healthy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { MatDialog, MatDialogRef, MatDialogState } from '@angular/material/dialo
import { MatTooltip } from '@angular/material/tooltip';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateModule } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { filter, Subscription, switchMap } from 'rxjs';
import { DirectoryServiceState } from 'app/enums/directory-service-state.enum';
import { Role } from 'app/enums/role.enum';
import { helptextTopbar } from 'app/helptext/topbar';
import { DirectoryServicesState } from 'app/interfaces/directory-services-state.interface';
import { AuthService } from 'app/modules/auth/auth.service';
import { IxIconComponent } from 'app/modules/ix-icon/ix-icon.component';
import {
DirectoryServicesMonitorComponent,
Expand Down Expand Up @@ -44,6 +46,7 @@ export class DirectoryServicesIndicatorComponent implements OnInit, OnDestroy {
private api: ApiService,
private matDialog: MatDialog,
private cdr: ChangeDetectorRef,
private auth: AuthService,
) { }

get isServicesMonitorOpen(): boolean {
Expand Down Expand Up @@ -72,12 +75,20 @@ export class DirectoryServicesIndicatorComponent implements OnInit, OnDestroy {

private loadDirectoryServicesStatus(): void {
// TODO: Sync endpoints
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!
One more thing, since these changes already affect related code, I suggest to sync 2 calls in this method as suggested by a TODO, if this is possible to do within this PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.api.call('directoryservices.get_state').pipe(untilDestroyed(this)).subscribe((state) => {
this.updateIconVisibility(state);
});
this.statusSubscription = this.api.subscribe('directoryservices.status').pipe(untilDestroyed(this)).subscribe((event) => {
this.updateIconVisibility(event.fields);
});
this.api.call('directoryservices.get_state')
.pipe(untilDestroyed(this))
.subscribe((state) => {
this.updateIconVisibility(state);
});
this.statusSubscription = this.auth.hasRole(Role.DirectoryServiceRead)
.pipe(
filter(Boolean),
switchMap(() => this.api.subscribe('directoryservices.status')),
untilDestroyed(this),
)
.subscribe((event) => {
this.updateIconVisibility(event.fields);
});
}

updateIconVisibility(servicesState: DirectoryServicesState): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<ix-modal-header
[requiredRoles]="requiredRoles"
[title]="'iSCSI Wizard' | translate"
[loading]="isLoading"
[loading]="isLoading()"
></ix-modal-header>

<mat-card class="card">
Expand Down Expand Up @@ -50,7 +50,7 @@
color="primary"
type="submit"
ixTest="save"
[disabled]="form.invalid || isLoading"
[disabled]="form.invalid || isLoading()"
>{{ 'Save' | translate }}</button>
}
</div>
Expand All @@ -77,7 +77,7 @@
color="primary"
type="submit"
ixTest="save"
[disabled]="form.invalid || isLoading"
[disabled]="form.invalid || isLoading()"
>{{ 'Save' | translate }}</button>
</div>
</mat-step>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('IscsiWizardComponent', () => {
let store$: Store<AppState>;

const slideInRef: SlideInRef<undefined, unknown> = {
close: jest.fn(),
close: jest.fn(() => true),
requireConfirmationWhen: jest.fn(),
getData: jest.fn(() => undefined),
};
Expand Down Expand Up @@ -120,7 +120,7 @@ describe('IscsiWizardComponent', () => {
jest.spyOn(store$, 'dispatch');
});

it('creates objects when wizard is submitted', fakeAsync(async () => {
it('iSCSI: creates objects when wizard is submitted', fakeAsync(async () => {
spectator.tick(100);

await form.fillForm({
Expand Down Expand Up @@ -191,4 +191,73 @@ describe('IscsiWizardComponent', () => {

expect(spectator.inject(SlideInRef).close).toHaveBeenCalled();
}));

it('fibre channel: creates objects when wizard is submitted', async () => {
await form.fillForm({
Name: 'test-name',
Device: 'Create New',
'Pool/Dataset': '/mnt/new_pool',
Size: 1024,
Portal: 'Create New',
Initiators: ['initiator1', 'initiator2'],
});

const addIpAddressButton = await loader.getHarness(IxListHarness.with({ label: 'IP Address' }));
await addIpAddressButton.pressAddButton();

await form.fillForm(
{
'IP Address': '::',
},
);

const saveButton = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
await saveButton.click();

expect(spectator.inject(ApiService).call).toHaveBeenNthCalledWith(8, 'pool.dataset.create', [{
name: 'new_pool/test-name',
type: 'VOLUME',
volsize: 1073741824,
}]);

expect(spectator.inject(ApiService).call).toHaveBeenNthCalledWith(9, 'iscsi.extent.create', [{
blocksize: 512,
disk: 'zvol/my+pool/test_zvol',
insecure_tpc: true,
name: 'test-name',
rpm: 'SSD',
type: 'DISK',
xen: false,
}]);

expect(spectator.inject(ApiService).call).toHaveBeenNthCalledWith(10, 'iscsi.portal.create', [{
comment: 'test-name',
listen: [{ ip: '::' }],
}]);

expect(spectator.inject(ApiService).call).toHaveBeenNthCalledWith(11, 'iscsi.initiator.create', [{
comment: 'test-name',
initiators: ['initiator1', 'initiator2'],
}]);

expect(spectator.inject(ApiService).call).toHaveBeenNthCalledWith(12, 'iscsi.target.create', [{
name: 'test-name',
mode: 'ISCSI',
groups: [{
auth: null,
authmethod: 'NONE',
initiator: 14,
portal: 13,
}],
}]);

expect(spectator.inject(ApiService).call).toHaveBeenNthCalledWith(13, 'iscsi.targetextent.create', [{
extent: 11,
target: 15,
}]);

expect(store$.dispatch).toHaveBeenCalledWith(checkIfServiceIsEnabled({ serviceName: ServiceName.Iscsi }));

expect(spectator.inject(SlideInRef).close).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit,
ChangeDetectionStrategy, Component, OnInit,
signal,
} from '@angular/core';
import { Validators, ReactiveFormsModule } from '@angular/forms';
import { MatButton } from '@angular/material/button';
Expand Down Expand Up @@ -90,9 +91,9 @@ import { ExtentWizardStepComponent } from './steps/extent-wizard-step/extent-wiz
],
})
export class IscsiWizardComponent implements OnInit {
isLoading = false;
toStop = false;
namesInUse: string[] = [];
isLoading = signal<boolean>(false);
toStop = signal<boolean>(false);
namesInUse = signal<string[]>([]);

createdZvol: Dataset | undefined;
createdExtent: IscsiExtent | undefined;
Expand All @@ -109,7 +110,7 @@ export class IscsiWizardComponent implements OnInit {
extent: this.fb.group({
name: ['', [
Validators.required,
forbiddenValues(this.namesInUse),
forbiddenValues(this.namesInUse()),
Validators.pattern(patterns.targetDeviceName),
]],
type: [IscsiExtentType.Disk, [Validators.required]],
Expand Down Expand Up @@ -252,17 +253,16 @@ export class IscsiWizardComponent implements OnInit {
private api: ApiService,
private errorHandler: ErrorHandlerService,
private dialogService: DialogService,
private cdr: ChangeDetectorRef,
private translate: TranslateService,
private loader: AppLoaderService,
private store$: Store<ServicesState>,
public slideInRef: SlideInRef<undefined, boolean>,
) {
this.iscsiService.getExtents().pipe(untilDestroyed(this)).subscribe((extents) => {
this.namesInUse.push(...extents.map((extent) => extent.name));
this.namesInUse.set(extents.map((extent) => extent.name));
});
this.iscsiService.getTargets().pipe(untilDestroyed(this)).subscribe((targets) => {
this.namesInUse.push(...targets.map((target) => target.name));
this.namesInUse.set(targets.map((target) => target.name));
});
}

Expand Down Expand Up @@ -326,8 +326,7 @@ export class IscsiWizardComponent implements OnInit {
}

rollBack(): void {
this.isLoading = false;
this.cdr.markForCheck();
this.isLoading.set(false);

const requests = [];

Expand Down Expand Up @@ -365,13 +364,13 @@ export class IscsiWizardComponent implements OnInit {
}

handleError(err: unknown): void {
this.toStop = true;
this.toStop.set(true);
this.dialogService.error(this.errorHandler.parseError(err));
}

async onSubmit(): Promise<void> {
this.isLoading = true;
this.toStop = false;
this.isLoading.set(true);
this.toStop.set(false);

this.createdZvol = undefined;
this.createdExtent = undefined;
Expand All @@ -387,7 +386,7 @@ export class IscsiWizardComponent implements OnInit {
);
}

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}
Expand All @@ -397,7 +396,7 @@ export class IscsiWizardComponent implements OnInit {
(err: unknown) => this.handleError(err),
);

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}
Expand All @@ -409,7 +408,7 @@ export class IscsiWizardComponent implements OnInit {
);
}

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}
Expand All @@ -421,7 +420,7 @@ export class IscsiWizardComponent implements OnInit {
);
}

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}
Expand All @@ -433,7 +432,7 @@ export class IscsiWizardComponent implements OnInit {
);
}

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}
Expand All @@ -443,7 +442,7 @@ export class IscsiWizardComponent implements OnInit {
(err: unknown) => this.handleError(err),
);

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}
Expand All @@ -456,15 +455,14 @@ export class IscsiWizardComponent implements OnInit {
).then(() => {}, (err: unknown) => this.handleError(err));
}

if (this.toStop) {
if (this.toStop()) {
this.rollBack();
return;
}

this.store$.dispatch(checkIfServiceIsEnabled({ serviceName: ServiceName.Iscsi }));

this.isLoading = false;
this.cdr.markForCheck();
this.isLoading.set(false);
this.slideInRef.close({ response: true, error: null });
}
}
3 changes: 2 additions & 1 deletion src/app/pages/sharing/iscsi/iscsi.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { async, Subject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { filter, map, startWith } from 'rxjs/operators';
import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive';
import { UiSearchDirective } from 'app/directives/ui-search.directive';
import { Role } from 'app/enums/role.enum';
Expand Down Expand Up @@ -49,6 +49,7 @@ export class IscsiComponent {
protected readonly needRefresh$ = new Subject<void>();

protected readonly navLinks$ = this.iscsiService.hasFibreChannel().pipe(
startWith(false),
map((hasFibreChannel) => {
const links = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@if (dataProvider.expandedRow; as target) {
<div class="detail-actions">
<button
*ixRequiresRoles="requiredRoles"
mat-button
ixTest="edit-target"
(click)="editTarget(target)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import { ApiService } from 'app/modules/websocket/api.service';
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CardExpandCollapseComponent,
MatCard,
MatCardContent,
MatCardHeader,
MatCardTitle,
NgxSkeletonLoaderModule,
TranslateModule,
MatCardContent,
CardExpandCollapseComponent,
],
})
export class ConnectionsCardComponent {
Expand Down
Loading
Loading