]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: fix delete subsystem issue 68160/head
authorSagar Gopale <sagar.gopale@ibm.com>
Wed, 1 Apr 2026 11:01:52 +0000 (16:31 +0530)
committerSagar Gopale <sagar.gopale@ibm.com>
Mon, 6 Apr 2026 10:01:47 +0000 (15:31 +0530)
Fixes: https://tracker.ceph.com/issues/75847
Signed-off-by: Sagar Gopale <sagar.gopale@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/delete-confirmation-modal/delete-confirmation-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/delete-confirmation-modal/delete-confirmation-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/delete-confirmation.model.ts

index 39b9c1b3b13a42b22c3d77f506ccb4b6e4a93a28..71c954d34ae0c33936d966f85078fe743e2a8cde 100644 (file)
@@ -175,7 +175,8 @@ export class NvmeofSubsystemsComponent extends ListWithDetails implements OnInit
       itemNames: [subsystem.nqn],
       actionDescription: 'delete',
       bodyContext: {
-        deletionMessage: $localize`Deleting <strong>${subsystem.nqn}</strong> will remove all associated configurations and resources. Dependent services may stop working. This action cannot be undone.`
+        deletionMessage: $localize`Deleting <strong>${subsystem.nqn}</strong> will remove all associated configurations and resources. Dependent services may stop working. This action cannot be undone.`,
+        forceDeleteAcknowledgementMessage: $localize`I understand this may remove resources still attached to this subsystem.`
       },
       submitActionObservable: () =>
         this.taskWrapper.wrapTaskAroundCall({
index 4e670f455b7faf9eeb2484ae72d51dd67954f6d7..70c3da05eb4af9d9d0d666738a350b9906dea5b0 100755 (executable)
@@ -174,9 +174,13 @@ describe('NvmeofService', () => {
 
     it('should call deleteSubsystem', () => {
       service.deleteSubsystem(mockNQN, mockGroupName).subscribe();
-      const req = httpTesting.expectOne(
-        `${API_PATH}/subsystem/${mockNQN}?gw_group=${mockGroupName}`
-      );
+      const req = httpTesting.expectOne((request) => {
+        return (
+          request.url === `${API_PATH}/subsystem/${mockNQN}` &&
+          request.params.get('gw_group') === mockGroupName &&
+          request.params.get('force') === 'true'
+        );
+      });
       expect(req.request.method).toBe('DELETE');
     });
     it('should call isSubsystemPresent', () => {
index 1cfdf54e656a1a5460c72c470c2a13920f6831a5..5d4573ab40f9100f0e78d851568693f5f18adc3a 100644 (file)
@@ -191,8 +191,12 @@ export class NvmeofService {
   }
 
   deleteSubsystem(subsystemNQN: string, group: string) {
-    return this.http.delete(`${API_PATH}/subsystem/${subsystemNQN}?gw_group=${group}`, {
-      observe: 'response'
+    return this.http.delete(`${API_PATH}/subsystem/${subsystemNQN}`, {
+      observe: 'response',
+      params: {
+        gw_group: group,
+        force: 'true'
+      }
     });
   }
 
index 0682cac60bed073c83011306a073847825643102..9a7c5b0246809fd28cea15ddcb6beef7e38fe87c 100644 (file)
                 <ng-container i18n>Enter the correct resource name.</ng-container>
               </span>
             </ng-template>
+            @if (bodyContext?.forceDeleteAcknowledgementMessage) {
+            <div class="form-item cds-mt-3">
+              <cds-checkbox id="force_delete_ack"
+                            formControlName="forceDeleteAck"
+                            [required]="true"
+                            ariaLabel="Forced delete acknowledgement">
+                {{ bodyContext.forceDeleteAcknowledgementMessage }}
+              </cds-checkbox>
+            </div>
+            }
           </ng-template>
         </div>
       </div>
                         [form]="deletionForm"
                         [submitText]="(actionDescription | titlecase) + ' ' + itemDescription"
                         [modalForm]="true"
-                        [disabled]="(submitDisabled$ | async) || deletionForm.disabled || (impact === impactEnum.high ? !deletionForm.controls.confirmInput.value : !deletionForm.controls.confirmation.value)"
+                        [disabled]="(submitDisabled$ | async) || deletionForm.disabled || (impact === impactEnum.high ? (!deletionForm.controls.confirmInput.value || !forceDeleteAckSatisfied) : !deletionForm.controls.confirmation.value)"
                         [submitBtnType]="(actionDescription === 'delete' || actionDescription === 'remove') ? 'danger' : 'primary'"></cd-form-button-panel>
 
 </cds-modal>
index 4b34d02a10ff490f0d4df66bed5347fed2024761..806ec57a826fe43d8e432258c11f6512029ff81f 100644 (file)
@@ -51,7 +51,7 @@ export class DeleteConfirmationModalComponent extends BaseModal implements OnIni
   }
 
   ngOnInit() {
-    const controls = {
+    const controls: Record<string, AbstractControl> = {
       impact: new UntypedFormControl(this.impact),
       confirmation: new UntypedFormControl(false, {
         validators: [
@@ -73,6 +73,13 @@ export class DeleteConfirmationModalComponent extends BaseModal implements OnIni
       })
     };
 
+    if (
+      this.impact === this.impactEnum.high &&
+      this.bodyContext?.forceDeleteAcknowledgementMessage
+    ) {
+      controls.forceDeleteAck = new UntypedFormControl(false, [Validators.requiredTrue]);
+    }
+
     if (this.childFormGroup) {
       controls['child'] = this.childFormGroup;
     }
@@ -96,6 +103,16 @@ export class DeleteConfirmationModalComponent extends BaseModal implements OnIni
     }
   }
 
+  get forceDeleteAckSatisfied(): boolean {
+    if (
+      !(this.impact === this.impactEnum.high && this.bodyContext?.forceDeleteAcknowledgementMessage)
+    ) {
+      return true;
+    }
+    const c = this.deletionForm?.get('forceDeleteAck');
+    return c ? !!c.value : true;
+  }
+
   matchResourceName(control: AbstractControl): ValidationErrors | null {
     if (!control.value) {
       return null;
index 101a4bd049a6c0da039213696a6e96a2e8ae89f0..7f4af3123ff9795047c1702ad364ffa79295dd95 100644 (file)
@@ -4,4 +4,6 @@ export interface DeleteConfirmationBodyContext {
   inputLabel?: string;
   inputPlaceholder?: string;
   deletionMessage?: string;
+  /** When set on a high-impact delete, user must check an extra acknowledgement before submit. */
+  forceDeleteAcknowledgementMessage?: string;
 }