]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: NVMe-oF Initiator Hostname Pre-validation 68224/head
authorSagar Gopale <sagar.gopale@ibm.com>
Mon, 6 Apr 2026 13:16:27 +0000 (18:46 +0530)
committerSagar Gopale <sagar.gopale@ibm.com>
Thu, 9 Apr 2026 06:45:28 +0000 (12:15 +0530)
Fixes: https://tracker.ceph.com/issues/75923
Signed-off-by: Sagar Gopale <sagar.gopale@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-initiators-form/nvmeof-initiators-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-initiators-form/nvmeof-initiators-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/tearsheet/tearsheet.component.ts

index 4ef4f03929b38ded2b12dd80cbd4a8252941989a..8ec7fbb023f8c8b996815e46a95c7c50afc86f86 100644 (file)
@@ -78,7 +78,7 @@ describe('NvmeofInitiatorsFormComponent', () => {
   describe('should test form', () => {
     beforeEach(() => {
       nvmeofService = TestBed.inject(NvmeofService);
-      spyOn(nvmeofService, 'addSubsystemInitiators').and.stub();
+      spyOn(nvmeofService, 'addSubsystemInitiators').and.returnValue(of({}));
     });
 
     it('should be creating request correctly', () => {
@@ -118,5 +118,21 @@ describe('NvmeofInitiatorsFormComponent', () => {
         hosts: [{ dhchap_key: '', host_nqn: 'host2' }]
       });
     });
+
+    it('should not submit when hostType is SPECIFIC and no host is provided', () => {
+      const subsystemNQN = 'nqn.test';
+      component.subsystemNQN = subsystemNQN;
+      component.group = 'test-group';
+
+      const payload: any = {
+        hostType: HOST_TYPE.SPECIFIC,
+        addedHosts: []
+      };
+
+      component.onSubmit(payload);
+
+      expect(nvmeofService.addSubsystemInitiators).not.toHaveBeenCalled();
+      expect(component.isSubmitLoading).toBe(false);
+    });
   });
 });
index cbb5513c6ecdd0c07a49593fb47d593279630d83..d25ffa1368a604177ac113861ba1aa93bbc42ac7 100644 (file)
@@ -109,10 +109,9 @@ export class NvmeofInitiatorsFormComponent implements OnInit {
   }
 
   onSubmit(payload: InitiatorsFormPayload) {
-    this.isSubmitLoading = true;
     const taskUrl = `nvmeof/initiator/add`;
-    const hostKeyList = payload.hostDchapKeyList || [];
-    const addedHosts = payload.addedHosts || [];
+    const hostKeyList = (payload.hostDchapKeyList || []).filter((host) => !!host?.host_nqn?.trim());
+    const addedHosts = (payload.addedHosts || []).filter((host) => !!host?.trim());
     const hosts =
       payload.hostType === HOST_TYPE.SPECIFIC
         ? hostKeyList.length
@@ -120,6 +119,25 @@ export class NvmeofInitiatorsFormComponent implements OnInit {
           : addedHosts.map((host_nqn: string) => ({ host_nqn, dhchap_key: '' }))
         : [];
 
+    if (payload.hostType === HOST_TYPE.SPECIFIC && hosts.length === 0) {
+      const hostStepIndex = Math.max(
+        this.tearsheet?.getStepIndexByLabel(STEP_LABELS.HOSTS) ?? 0,
+        0
+      );
+      const hostStepForm = this.tearsheet?.stepContents?.toArray()?.[hostStepIndex]?.stepComponent
+        ?.formGroup;
+
+      hostStepForm?.markAllAsTouched();
+      hostStepForm?.get('hostname')?.markAsTouched();
+      hostStepForm?.get('hostname')?.updateValueAndValidity({ emitEvent: true });
+      if (this.tearsheet) {
+        this.tearsheet.currentStep = hostStepIndex;
+      }
+      return;
+    }
+
+    this.isSubmitLoading = true;
+
     const request: SubsystemInitiatorRequest = {
       allow_all: payload.hostType === HOST_TYPE.ALL,
       hosts,
index e3c5fa46d428eee547403bfe49ffd3685511b298..39c7cc304a0c04fb61916877d92e2a83bbca49c4 100644 (file)
@@ -177,6 +177,15 @@ export class TearsheetComponent implements OnInit, AfterViewInit, OnDestroy {
   }
 
   handleSubmit() {
+    this.stepContents?.forEach((wrapper, index) => {
+      const form = wrapper.stepComponent?.formGroup;
+      if (!form) return;
+
+      form.markAllAsTouched();
+      form.updateValueAndValidity({ emitEvent: true });
+      this._updateStepInvalid(index, form.invalid);
+    });
+
     if (this.steps.some((step) => step?.invalid)) return;
 
     const mergedPayloads = this.getMergedPayload();