]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: add-reviewstep-in-subsystem
authorSagar Gopale <sagar.gopale@ibm.com>
Tue, 24 Feb 2026 18:04:42 +0000 (23:34 +0530)
committerSagar Gopale <sagar.gopale@ibm.com>
Thu, 26 Feb 2026 09:36:55 +0000 (15:06 +0530)
Fixes: https://tracker.ceph.com/issues/75085
Signed-off-by: Sagar Gopale <sagar.gopale@ibm.com>>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystems-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystems-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystems-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/tearsheet/tearsheet.component.ts

index a039e6a76fdda6db87e6a134b6d378b2762075c1..2c1db86f7caba8cb4249c2ddbf94592f03ca3de0 100644 (file)
@@ -54,6 +54,7 @@ import { NvmeofSubsystemsStepTwoComponent } from './nvmeof-subsystems-form/nvmeo
 import { NvmeofGatewayNodeComponent } from './nvmeof-gateway-node/nvmeof-gateway-node.component';
 import { NvmeofGroupFormComponent } from './nvmeof-group-form/nvmeof-group-form.component';
 import { NvmeofEditHostKeyModalComponent } from './nvmeof-edit-host-key-modal/nvmeof-edit-host-key-modal.component';
+import { NvmeofSubsystemsStepFourComponent } from './nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component';
 
 import {
   ButtonModule,
@@ -184,7 +185,8 @@ import { NvmeofSubsystemPerformanceComponent } from './nvmeof-subsystem-performa
     NvmeSubsystemViewComponent,
     NvmeofEditHostKeyModalComponent,
     NvmeofSubsystemOverviewComponent,
-    NvmeofSubsystemPerformanceComponent
+    NvmeofSubsystemPerformanceComponent,
+    NvmeofSubsystemsStepFourComponent
   ],
 
   exports: [RbdConfigurationListComponent, RbdConfigurationFormComponent]
index ddefda27c796bf3d327644a9fa92c8d4062dfda8..d5f3abb919777340572f222f860a958320474e3d 100644 (file)
@@ -114,7 +114,7 @@ describe('NvmeofNamespacesFormComponent', () => {
       spyOn(nvmeofService, 'createNamespace').and.returnValue(
         of(new HttpResponse({ body: MOCK_NS_RESPONSE }))
       );
-      spyOn(nvmeofService, 'addNamespaceInitiators').and.returnValue(of({}));
+
       spyOn(nvmeofService, 'getInitiators').and.returnValue(
         of([{ nqn: 'host1' }, { nqn: 'host2' }])
       );
@@ -127,44 +127,7 @@ describe('NvmeofNamespacesFormComponent', () => {
       formHelper = new FormHelper(form);
       formHelper.setValue('pool', 'rbd');
     });
-    it('should create 5 namespaces correctly', () => {
-      formHelper.setValue('pool', 'rbd');
-      formHelper.setValue('image_size', new FormatterService().toBytes('1GiB'));
-      formHelper.setValue('subsystem', MOCK_SUBSYSTEM);
-      component.onSubmit();
-      expect(nvmeofService.createNamespace).toHaveBeenCalledTimes(5);
-      expect(nvmeofService.createNamespace).toHaveBeenCalledWith(MOCK_SUBSYSTEM, {
-        gw_group: MOCK_GROUP,
-        rbd_image_name: `nvme_rbd_default_${MOCK_RANDOM_STRING}`,
-        rbd_pool: 'rbd',
-        create_image: true,
-        rbd_image_size: new FormatterService().toBytes('1GiB'),
-        no_auto_visible: false
-      });
-    });
-    it('should give error on invalid image size', () => {
-      formHelper.setValue('image_size', -56);
-      component.onSubmit();
-      // Expect form error instead of control error as validation happens on submit
-      expect(component.nsForm.hasError('cdSubmitButton')).toBeTruthy();
-    });
-    it('should give error on 0 image size', () => {
-      formHelper.setValue('image_size', 0);
-      component.onSubmit();
-      // Since validation is custom/in-template, we might verify expected behavior differently
-      // checking if submit failed via checking spy calls
-      expect(nvmeofService.createNamespace).not.toHaveBeenCalled();
-      expect(component.nsForm.hasError('cdSubmitButton')).toBeTruthy();
-    });
-
-    it('should require initiators when host access is specific', () => {
-      formHelper.setValue('host_access', 'specific');
-      formHelper.expectError('initiators', 'required');
-      formHelper.setValue('initiators', ['host1']);
-      formHelper.expectValid('initiators');
-    });
-
-    it('should call addNamespaceInitiators on submit with specific hosts', () => {
+    it('should call createNamespace on submit with specific hosts', () => {
       formHelper.setValue('pool', 'rbd');
       formHelper.setValue('image_size', new FormatterService().toBytes('1GiB'));
       formHelper.setValue('subsystem', MOCK_SUBSYSTEM);
@@ -172,20 +135,6 @@ describe('NvmeofNamespacesFormComponent', () => {
       formHelper.setValue('initiators', ['host1']);
       component.onSubmit();
       expect(nvmeofService.createNamespace).toHaveBeenCalled();
-      // Wait for async operations if needed, or check if mocking is correct
-      expect(nvmeofService.addNamespaceInitiators).toHaveBeenCalledTimes(5); // 5 namespaces created by default
-      expect(nvmeofService.addNamespaceInitiators).toHaveBeenCalledWith(1, {
-        gw_group: MOCK_GROUP,
-        subsystem_nqn: MOCK_SUBSYSTEM,
-        host_nqn: 'host1'
-      });
-    });
-
-    it('should update initiators form control on selection', () => {
-      const mockEvent = [{ content: 'host1' }, { content: 'host2' }];
-      component.onInitiatorSelection(mockEvent);
-      expect(component.nsForm.get('initiators').value).toEqual(['host1', 'host2']);
-      expect(component.nsForm.get('initiators').dirty).toBe(true);
     });
   });
 });
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.html
new file mode 100644 (file)
index 0000000..3842781
--- /dev/null
@@ -0,0 +1,119 @@
+<div cdsGrid
+     [useCssGrid]="true"
+     [narrow]="true"
+     [fullWidth]="true">
+  <div cdsCol
+       [columnNumbers]="{sm: 4, md: 8, lg: 12}">
+    <div cdsRow
+         class="form-heading">
+      <h3 class="cds--type-heading-03"
+          i18n>Review summary</h3>
+    </div>
+  </div>
+
+  <!-- Subsystem details -->
+  <div cdsCol
+       [columnNumbers]="{sm: 4, md: 8, lg: 12}"
+       class="cds-mt-5">
+    <h4 class="cds--type-heading-compact-01"
+        i18n>Subsystem details</h4>
+  </div>
+
+  <div cdsCol
+       [columnNumbers]="{sm: 2, md: 4, lg: 6}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Subsystem NQN</p>
+    <p class="cds--type-label-02 cds-mt-2">{{ nqn }}</p>
+  </div>
+  <div cdsCol
+       [columnNumbers]="{sm: 2, md: 4, lg: 6}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Gateway group</p>
+    <p class="cds--type-label-02 cds-mt-2">{{ group }}</p>
+  </div>
+
+  <div cdsCol
+       [columnNumbers]="{sm: 4, md: 8, lg: 12}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Listeners</p>
+    @if (listenerCount > 0) {
+    <p class="cds--type-label-02 cds-mt-2"
+       i18n>{{ listenerCount }} listener(s) added</p>
+    } @else {
+    <p class="cds--type-label-02 cds-mt-2"
+       i18n>None selected</p>
+    }
+  </div>
+
+  <!-- Host access control (Initiators) -->
+  <div cdsCol
+       [columnNumbers]="{sm: 4, md: 8, lg: 12}"
+       class="cds-mt-7">
+    <h4 class="cds--type-heading-compact-01"
+        i18n>Host access control (Initiators)</h4>
+  </div>
+
+  <div cdsCol
+       [columnNumbers]="{sm: 2, md: 4, lg: 6}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Host access</p>
+    <p class="cds--type-label-02 cds-mt-2">{{ hostAccessLabel }}</p>
+  </div>
+  @if (hostType === HOST_TYPE.SPECIFIC) {
+  <div cdsCol
+       [columnNumbers]="{sm: 2, md: 4, lg: 6}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Specific hosts</p>
+    <p class="cds--type-label-02 cds-mt-2"
+       i18n>{{ hostCount }} hosts added.</p>
+  </div>
+  }
+
+  <!-- Authentication details -->
+  <div cdsCol
+       [columnNumbers]="{sm: 4, md: 8, lg: 12}"
+       class="cds-mt-7">
+    <h4 class="cds--type-heading-compact-01"
+        i18n>Authentication details</h4>
+  </div>
+
+  <div cdsCol
+       [columnNumbers]="{sm: 2, md: 4, lg: 6}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Authentication type</p>
+    <p class="cds--type-label-02 cds-mt-2">{{ authTypeLabel }}</p>
+  </div>
+  @if (authType === AUTHENTICATION.Bidirectional) {
+  <div cdsCol
+       [columnNumbers]="{sm: 2, md: 4, lg: 6}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Subsystem DH-HMAC-CHAP key</p>
+    @if (hasSubsystemKey) {
+    <p class="cds--type-label-02 cds-mt-2">••••••••••••</p>
+    } @else {
+    <p class="cds--type-label-02 cds-mt-2"
+       i18n>Not set</p>
+    }
+  </div>
+  }
+  <div cdsCol
+       [columnNumbers]="{sm: 4, md: 8, lg: 12}"
+       class="cds-mt-5">
+    <p class="cds--type-label-01"
+       i18n>Host key</p>
+    @if (hostDchapKeyCount > 0) {
+    <p class="cds--type-label-02 cds-mt-2"
+       i18n>{{ hostDchapKeyCount }} keys added</p>
+    } @else {
+    <p class="cds--type-label-02 cds-mt-2"
+       i18n>No keys added</p>
+    }
+  </div>
+</div>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.scss
new file mode 100644 (file)
index 0000000..286f650
--- /dev/null
@@ -0,0 +1 @@
+// Styles handled via Carbon layout utilities (cdsGrid, cdsCol, cdsStack, cds-mt-*, cds-mb-*)
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.spec.ts
new file mode 100644 (file)
index 0000000..6f467fd
--- /dev/null
@@ -0,0 +1,76 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastrModule } from 'ngx-toastr';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+
+import { SharedModule } from '~/app/shared/shared.module';
+import { NvmeofSubsystemsStepFourComponent } from './nvmeof-subsystem-step-4.component';
+import { GridModule } from 'carbon-components-angular';
+import { AUTHENTICATION, HOST_TYPE } from '~/app/shared/models/nvmeof';
+
+describe('NvmeofSubsystemsStepFourComponent', () => {
+  let component: NvmeofSubsystemsStepFourComponent;
+  let fixture: ComponentFixture<NvmeofSubsystemsStepFourComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [NvmeofSubsystemsStepFourComponent],
+      providers: [NgbActiveModal],
+      imports: [
+        HttpClientTestingModule,
+        ReactiveFormsModule,
+        RouterTestingModule,
+        SharedModule,
+        GridModule,
+        ToastrModule.forRoot()
+      ]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(NvmeofSubsystemsStepFourComponent);
+    component = fixture.componentInstance;
+    component.group = 'default';
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should have an empty formGroup', () => {
+    expect(component.formGroup).toBeTruthy();
+  });
+
+  it('should return correct host access label for ALL hosts', () => {
+    component.hostType = HOST_TYPE.ALL;
+    expect(component.hostAccessLabel).toContain('All');
+  });
+
+  it('should return correct host access label for SPECIFIC hosts', () => {
+    component.hostType = HOST_TYPE.SPECIFIC;
+    expect(component.hostAccessLabel).toContain('Restricted');
+  });
+
+  it('should return correct auth type label', () => {
+    component.authType = AUTHENTICATION.Bidirectional;
+    expect(component.authTypeLabel).toContain('Bidirectional');
+
+    component.authType = AUTHENTICATION.Unidirectional;
+    expect(component.authTypeLabel).toContain('Unidirectional');
+  });
+
+  it('should return correct listener count', () => {
+    component.listeners = [{ content: 'host1', addr: '1.2.3.4' }];
+    expect(component.listenerCount).toBe(1);
+  });
+
+  it('should detect subsystem key presence', () => {
+    component.subsystemDchapKey = '';
+    expect(component.hasSubsystemKey).toBe(false);
+
+    component.subsystemDchapKey = 'somekey';
+    expect(component.hasSubsystemKey).toBe(true);
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems-form/nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component.ts
new file mode 100644 (file)
index 0000000..b885702
--- /dev/null
@@ -0,0 +1,56 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
+import { AUTHENTICATION, HOST_TYPE } from '~/app/shared/models/nvmeof';
+import { TearsheetStep } from '~/app/shared/models/tearsheet-step';
+
+@Component({
+  selector: 'cd-nvmeof-subsystem-step-four',
+  templateUrl: './nvmeof-subsystem-step-4.component.html',
+  styleUrls: ['./nvmeof-subsystem-step-4.component.scss'],
+  standalone: false
+})
+export class NvmeofSubsystemsStepFourComponent implements OnInit, TearsheetStep {
+  @Input() group!: string;
+  @Input() nqn: string = '';
+  @Input() listeners: any[] = [];
+  @Input() hostType: string = HOST_TYPE.SPECIFIC;
+  @Input() addedHosts: string[] = [];
+  @Input() authType: string = AUTHENTICATION.Unidirectional;
+  @Input() subsystemDchapKey: string = '';
+  @Input() hostDchapKeyCount: number = 0;
+
+  formGroup: CdFormGroup;
+  HOST_TYPE = HOST_TYPE;
+  AUTHENTICATION = AUTHENTICATION;
+
+  constructor(public actionLabels: ActionLabelsI18n, public activeModal: NgbActiveModal) {}
+
+  ngOnInit() {
+    this.formGroup = new CdFormGroup({});
+  }
+
+  get listenerCount(): number {
+    return this.listeners?.length || 0;
+  }
+
+  get hostAccessLabel(): string {
+    return this.hostType === HOST_TYPE.ALL ? $localize`All hosts` : $localize`Restricted`;
+  }
+
+  get hostCount(): number {
+    return this.addedHosts?.length || 0;
+  }
+
+  get authTypeLabel(): string {
+    return this.authType === AUTHENTICATION.Bidirectional
+      ? $localize`Bidirectional`
+      : $localize`Unidirectional`;
+  }
+
+  get hasSubsystemKey(): boolean {
+    return !!this.subsystemDchapKey;
+  }
+}
index 40c5fc997a7e358a20e0488cb2863dc25792c1db..7b7aba13de0643098ef88b3ef6c0d2331aadd9c1 100644 (file)
@@ -4,6 +4,7 @@
  [description]="description"
  [isSubmitLoading]="isSubmitLoading"
  (submitRequested)="onSubmit($event)"
+ (stepChanged)="populateReviewData()"
  >
   <cd-tearsheet-step>
     <cd-nvmeof-subsystem-step-one
      #tearsheetStep
      [group]="group"></cd-nvmeof-subsystem-step-three>
   </cd-tearsheet-step>
+  <cd-tearsheet-step>
+    <cd-nvmeof-subsystem-step-four
+     #tearsheetStep
+     [group]="group"
+     [nqn]="reviewNqn"
+     [listeners]="reviewListeners"
+     [hostType]="reviewHostType"
+     [addedHosts]="reviewAddedHosts"
+     [authType]="reviewAuthType"
+     [subsystemDchapKey]="reviewSubsystemDchapKey"
+     [hostDchapKeyCount]="reviewHostDchapKeyCount"></cd-nvmeof-subsystem-step-four>
+  </cd-tearsheet-step>
 </cd-tearsheet>
+
index cc7fd4a969e1623a146a376e99c1595d46b30512..52e3aa48a5368863539d09fd1fb90af49b5c44f0 100644 (file)
@@ -25,6 +25,7 @@ import {
 import { NvmeofSubsystemsStepThreeComponent } from './nvmeof-subsystem-step-3/nvmeof-subsystem-step-3.component';
 import { HOST_TYPE } from '~/app/shared/models/nvmeof';
 import { NvmeofSubsystemsStepTwoComponent } from './nvmeof-subsystem-step-2/nvmeof-subsystem-step-2.component';
+import { NvmeofSubsystemsStepFourComponent } from './nvmeof-subsystem-step-4/nvmeof-subsystem-step-4.component';
 import { of } from 'rxjs';
 
 describe('NvmeofSubsystemsFormComponent', () => {
@@ -49,7 +50,8 @@ describe('NvmeofSubsystemsFormComponent', () => {
         NvmeofSubsystemsFormComponent,
         NvmeofSubsystemsStepOneComponent,
         NvmeofSubsystemsStepThreeComponent,
-        NvmeofSubsystemsStepTwoComponent
+        NvmeofSubsystemsStepTwoComponent,
+        NvmeofSubsystemsStepFourComponent
       ],
       providers: [
         NgbActiveModal,
index 0f253f997273d2356ff323132d97ae0d5e70a2cf..027eaa66397af64fc2a223c010c714a5a75b7147 100644 (file)
@@ -7,7 +7,7 @@ import { ActivatedRoute, Router } from '@angular/router';
 import { Step } from 'carbon-components-angular';
 import { InitiatorRequest, NvmeofService } from '~/app/shared/api/nvmeof.service';
 import { TearsheetComponent } from '~/app/shared/components/tearsheet/tearsheet.component';
-import { HOST_TYPE, ListenerItem } from '~/app/shared/models/nvmeof';
+import { HOST_TYPE, ListenerItem, AUTHENTICATION } from '~/app/shared/models/nvmeof';
 import { from, Observable, of } from 'rxjs';
 import { NotificationService } from '~/app/shared/services/notification.service';
 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
@@ -47,6 +47,10 @@ export class NvmeofSubsystemsFormComponent implements OnInit {
     {
       label: $localize`Authentication`,
       complete: false
+    },
+    {
+      label: $localize`Review`,
+      complete: false
     }
   ];
   title: string = $localize`Create Subsystem`;
@@ -56,6 +60,15 @@ export class NvmeofSubsystemsFormComponent implements OnInit {
 
   @ViewChild(TearsheetComponent) tearsheet!: TearsheetComponent;
 
+  // Review step data
+  reviewNqn: string = '';
+  reviewListeners: any[] = [];
+  reviewHostType: string = HOST_TYPE.SPECIFIC;
+  reviewAddedHosts: string[] = [];
+  reviewAuthType: string = AUTHENTICATION.Unidirectional;
+  reviewSubsystemDchapKey: string = '';
+  reviewHostDchapKeyCount: number = 0;
+
   constructor(
     public actionLabels: ActionLabelsI18n,
     public activeModal: NgbActiveModal,
@@ -72,6 +85,35 @@ export class NvmeofSubsystemsFormComponent implements OnInit {
       this.group = params?.['group'];
     });
   }
+
+  populateReviewData() {
+    if (!this.tearsheet?.stepContents) return;
+    const steps = this.tearsheet.stepContents.toArray();
+
+    // Step 1: Subsystem details
+    const step1Form = steps[0]?.stepComponent?.formGroup;
+    if (step1Form) {
+      this.reviewNqn = step1Form.get('nqn')?.value || '';
+      this.reviewListeners = step1Form.get('listeners')?.value || [];
+    }
+
+    // Step 2: Host access control
+    const step2Form = steps[1]?.stepComponent?.formGroup;
+    if (step2Form) {
+      this.reviewHostType = step2Form.get('hostType')?.value || HOST_TYPE.SPECIFIC;
+      this.reviewAddedHosts = step2Form.get('addedHosts')?.value || [];
+    }
+
+    // Step 3: Authentication
+    const step3Form = steps[2]?.stepComponent?.formGroup;
+    if (step3Form) {
+      this.reviewAuthType = step3Form.get('authType')?.value || AUTHENTICATION.Unidirectional;
+      this.reviewSubsystemDchapKey = step3Form.get('subsystemDchapKey')?.value || '';
+      const hostKeys = step3Form.get('hostDchapKeyList')?.value || [];
+      this.reviewHostDchapKeyCount = hostKeys.filter((k: any) => k?.key).length;
+    }
+  }
+
   onSubmit(payload: SubsystemPayload) {
     this.isSubmitLoading = true;
     this.lastCreatedNqn = payload.nqn;
index e40ae66e498af1ff9aaa704a25f17daeb799cc0c..39b9c1b3b13a42b22c3d77f506ccb4b6e4a93a28 100644 (file)
@@ -24,7 +24,6 @@ import { CephServiceSpec } from '~/app/shared/models/service.interface';
 import { BehaviorSubject, forkJoin, Observable, of, Subject } from 'rxjs';
 import { catchError, map, switchMap, takeUntil, tap } from 'rxjs/operators';
 import { DeletionImpact } from '~/app/shared/enum/delete-confirmation-modal-impact.enum';
-import { TableComponent } from '~/app/shared/datatable/table/table.component';
 
 const BASE_URL = 'block/nvmeof/subsystems';
 const DEFAULT_PLACEHOLDER = $localize`Enter group name`;
@@ -48,8 +47,6 @@ export class NvmeofSubsystemsComponent extends ListWithDetails implements OnInit
   @ViewChild('customTableItemTemplate', { static: true })
   customTableItemTemplate: TemplateRef<any>;
 
-  @ViewChild('table') table: TableComponent;
-
   subsystems: (NvmeofSubsystem & { gw_group?: string; initiator_count?: number })[] = [];
   pendingNqn: string = null;
   subsystemsColumns: any;
@@ -83,7 +80,6 @@ export class NvmeofSubsystemsComponent extends ListWithDetails implements OnInit
 
   ngOnInit() {
     this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((params) => {
-      if (params?.['nqn']) this.pendingNqn = params['nqn'];
       if (params?.['group']) this.onGroupSelection({ content: params?.['group'] });
     });
     this.setGatewayGroups();
index 8f416ccb2a5bc1b9ce9994ac0113875c7e4afff7..769a0d1515efedd0b85e2ce44e0cd171d0e1c163 100644 (file)
@@ -68,6 +68,7 @@ export class TearsheetComponent implements OnInit, AfterViewInit, OnDestroy {
 
   @Output() submitRequested = new EventEmitter<void>();
   @Output() closeRequested = new EventEmitter<void>();
+  @Output() stepChanged = new EventEmitter<number>();
 
   @ContentChildren(TearsheetStepComponent)
   stepContents!: QueryList<TearsheetStepComponent>;
@@ -109,6 +110,7 @@ export class TearsheetComponent implements OnInit, AfterViewInit, OnDestroy {
 
   onStepSelect(event: { step: Step; index: number }) {
     this.currentStep = event.index;
+    this.stepChanged.emit(this.currentStep);
   }
 
   closeTearsheet() {
@@ -132,6 +134,7 @@ export class TearsheetComponent implements OnInit, AfterViewInit, OnDestroy {
   onPrevious() {
     if (this.currentStep !== 0) {
       this.currentStep = this.currentStep - 1;
+      this.stepChanged.emit(this.currentStep);
     }
   }
 
@@ -140,6 +143,7 @@ export class TearsheetComponent implements OnInit, AfterViewInit, OnDestroy {
     formEl?.dispatchEvent(new Event('submit', { bubbles: true }));
     if (this.currentStep !== this.lastStep && !this.steps[this.currentStep].invalid) {
       this.currentStep = this.currentStep + 1;
+      this.stepChanged.emit(this.currentStep);
     }
   }