]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: fix create snapshot is disabled for rbd images
authorNizamudeen A <nia@redhat.com>
Fri, 19 May 2023 11:18:14 +0000 (16:48 +0530)
committerNizamudeen A <nia@redhat.com>
Fri, 26 May 2023 06:30:59 +0000 (12:00 +0530)
also the rename and rollback option in the snapshot list is enabled even if there isn't anything in the list

also disabled the Mirror Image Snapshot option in the Create RBD
Snapshot form (only shown for images configured with Snapshot mirroring)
with the helpe

Fixes: https://tracker.ceph.com/issues/61296
Signed-off-by: Nizamudeen A <nia@redhat.com>
(cherry picked from commit 896ae75901b564677ea1611a89f4d61da881a6e1)

src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form-modal.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts

index fb246a44451b10849378586cb82c5f17114db0fe..e84ecab695a6c7349e1f49f83ca807c05cea7169 100644 (file)
                    autofocus>
             <span class="invalid-feedback"
                   *ngIf="snapshotForm.showError('snapshotName', formDir, 'required')"
-                  i18n>This field is required.</span><br><br>
+                  i18n>This field is required.</span>
             <span *ngIf="((mirroring === 'snapshot') ? true : null) && (snapshotForm.getValue('mirrorImageSnapshot') === true) ? true: null"
                   i18n>Snapshot mode is enabled on image <b>{{ imageName }}</b>: snapshot names are auto generated</span>
           </div>
         </div>
-        <div *ngIf="(mirroring === 'snapshot') ? true : null">
-          <div class="form-group row">
+        <ng-container *ngIf="(mirroring === 'snapshot') ? true : null">
+          <div class="form-group row"
+               *ngIf="peerConfigured$ | async as peerConfigured">
             <div class="cd-col-form-offset">
               <div class="custom-control custom-checkbox">
                 <input type="checkbox"
                        formControlName="mirrorImageSnapshot"
                        name="mirrorImageSnapshot"
                        id="mirrorImageSnapshot"
+                       [attr.disabled]="!(peerConfigured.length > 0) ? true : null"
                        (change)="onMirrorCheckBoxChange()">
                 <label for="mirrorImageSnapshot"
                        class="custom-control-label"
                        i18n>Mirror Image Snapshot</label>
+                <cd-helper i18n
+                           *ngIf="!peerConfigured.length > 0">The peer must be registered to do this action.</cd-helper>
               </div>
             </div>
           </div>
-        </div>
+        </ng-container>
       </div>
       <div class="modal-footer">
         <cd-form-button-panel (submitActionEvent)="submit()"
index b6681ec510a5a925e7dbc3f7c5d9677137608c0d..5e7b04b9f25af244604c280ff83b2537fcf5fb25 100644 (file)
@@ -11,10 +11,13 @@ import { PipesModule } from '~/app/shared/pipes/pipes.module';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { configureTestBed } from '~/testing/unit-test-helper';
 import { RbdSnapshotFormModalComponent } from './rbd-snapshot-form-modal.component';
+import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
+import { of } from 'rxjs';
 
 describe('RbdSnapshotFormModalComponent', () => {
   let component: RbdSnapshotFormModalComponent;
   let fixture: ComponentFixture<RbdSnapshotFormModalComponent>;
+  let rbdMirrorService: RbdMirroringService;
 
   configureTestBed({
     imports: [
@@ -32,6 +35,7 @@ describe('RbdSnapshotFormModalComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(RbdSnapshotFormModalComponent);
     component = fixture.componentInstance;
+    rbdMirrorService = TestBed.inject(RbdMirroringService);
   });
 
   it('should create', () => {
@@ -59,4 +63,22 @@ describe('RbdSnapshotFormModalComponent', () => {
     const button = fixture.debugElement.nativeElement.querySelector('cd-submit-button');
     expect(button.textContent).toBe('Rename RBD Snapshot');
   });
+
+  it('should enable the mirror image snapshot creation when peer is configured', () => {
+    spyOn(rbdMirrorService, 'getPeerForPool').and.returnValue(of(['test_peer']));
+    component.mirroring = 'snapshot';
+    component.ngOnInit();
+    fixture.detectChanges();
+    const radio = fixture.debugElement.nativeElement.querySelector('#mirrorImageSnapshot');
+    expect(radio.disabled).toBe(false);
+  });
+
+  it('should disable the mirror image snapshot creation when peer is not configured', () => {
+    spyOn(rbdMirrorService, 'getPeerForPool').and.returnValue(of([]));
+    component.mirroring = 'snapshot';
+    component.ngOnInit();
+    fixture.detectChanges();
+    const radio = fixture.debugElement.nativeElement.querySelector('#mirrorImageSnapshot');
+    expect(radio.disabled).toBe(true);
+  });
 });
index 9fc5179e23b54932aba32aa85a1baac23c545b1b..f3665f6d0495e8ee9896aedff800409b6d3b8bf8 100644 (file)
@@ -1,8 +1,9 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { FormControl, Validators } from '@angular/forms';
 
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
-import { Subject } from 'rxjs';
+import { Observable, Subject } from 'rxjs';
+import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
 
 import { RbdService } from '~/app/shared/api/rbd.service';
 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
@@ -17,7 +18,7 @@ import { TaskManagerService } from '~/app/shared/services/task-manager.service';
   templateUrl: './rbd-snapshot-form-modal.component.html',
   styleUrls: ['./rbd-snapshot-form-modal.component.scss']
 })
-export class RbdSnapshotFormModalComponent {
+export class RbdSnapshotFormModalComponent implements OnInit {
   poolName: string;
   namespace: string;
   imageName: string;
@@ -32,12 +33,15 @@ export class RbdSnapshotFormModalComponent {
 
   public onSubmit: Subject<string> = new Subject();
 
+  peerConfigured$: Observable<any>;
+
   constructor(
     public activeModal: NgbActiveModal,
     private rbdService: RbdService,
     private taskManagerService: TaskManagerService,
     private notificationService: NotificationService,
-    private actionLabels: ActionLabelsI18n
+    private actionLabels: ActionLabelsI18n,
+    private rbdMirrorService: RbdMirroringService
   ) {
     this.action = this.actionLabels.CREATE;
     this.resource = $localize`RBD Snapshot`;
@@ -53,18 +57,22 @@ export class RbdSnapshotFormModalComponent {
     });
   }
 
+  ngOnInit(): void {
+    this.peerConfigured$ = this.rbdMirrorService.getPeerForPool(this.poolName);
+  }
+
   setSnapName(snapName: string) {
     this.snapName = snapName;
-    if (this.mirroring !== 'snapshot') {
-      this.snapshotForm.get('snapshotName').setValue(snapName);
-    } else {
-      this.snapshotForm.get('snapshotName').clearValidators();
-    }
+    this.snapshotForm.get('snapshotName').setValue(snapName);
   }
 
   onMirrorCheckBoxChange() {
     if (this.snapshotForm.getValue('mirrorImageSnapshot') === true) {
       this.snapshotForm.get('snapshotName').setValue('');
+      this.snapshotForm.get('snapshotName').clearValidators();
+    } else {
+      this.snapshotForm.get('snapshotName').setValue(this.snapName);
+      this.snapshotForm.get('snapshotName').setValidators([Validators.required]);
     }
   }
 
index cc0d61f91aa0720d63e933059b108cbe92390628..8b40111b8c8cb655586b4ea90c863ada4af02ea0 100644 (file)
@@ -35,7 +35,8 @@ export class RbdSnapshotActionsModel {
       permission: 'update',
       icon: Icons.edit,
       name: actionLabels.RENAME,
-      disable: (selection: CdTableSelection) => this.disableForMirrorSnapshot(selection)
+      disable: (selection: CdTableSelection) =>
+        this.disableForMirrorSnapshot(selection) || !selection.hasSingleSelection
     };
     this.protect = {
       permission: 'update',
@@ -76,7 +77,8 @@ export class RbdSnapshotActionsModel {
       permission: 'update',
       icon: Icons.undo,
       name: actionLabels.ROLLBACK,
-      disable: (selection: CdTableSelection) => this.disableForMirrorSnapshot(selection)
+      disable: (selection: CdTableSelection) =>
+        this.disableForMirrorSnapshot(selection) || !selection.hasSingleSelection
     };
     this.deleteSnap = {
       permission: 'delete',
index 0d89f2e2a0598f3bbf0e7d3c1aadcb1b5304519c..797fc35a11c6394a27a20b0b28784a3aa392d63e 100644 (file)
@@ -7,7 +7,6 @@ import { NgbModalModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
 import { MockComponent } from 'ng-mocks';
 import { ToastrModule } from 'ngx-toastr';
 import { Subject, throwError as observableThrowError } from 'rxjs';
-import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
 
 import { RbdService } from '~/app/shared/api/rbd.service';
 import { ComponentsModule } from '~/app/shared/components/components.module';
@@ -86,7 +85,6 @@ describe('RbdSnapshotListComponent', () => {
   describe('api delete request', () => {
     let called: boolean;
     let rbdService: RbdService;
-    let rbdMirroringService: RbdMirroringService;
     let notificationService: NotificationService;
     let authStorageService: AuthStorageService;
 
@@ -95,7 +93,6 @@ describe('RbdSnapshotListComponent', () => {
       const modalService = TestBed.inject(ModalService);
       const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
       called = false;
-      rbdMirroringService = new RbdMirroringService(null, null);
       rbdService = new RbdService(null, null);
       notificationService = new NotificationService(null, null, null);
       authStorageService = new AuthStorageService();
@@ -106,7 +103,6 @@ describe('RbdSnapshotListComponent', () => {
         null,
         null,
         rbdService,
-        rbdMirroringService,
         null,
         notificationService,
         null,
@@ -205,7 +201,8 @@ describe('RbdSnapshotListComponent', () => {
           null,
           null,
           null,
-          TestBed.inject(ActionLabelsI18n)
+          TestBed.inject(ActionLabelsI18n),
+          null
         );
         ref.componentInstance.onSubmit = new Subject();
         return ref;
index 81e2f4e0149add89277b3b4ae8797f990f999cbf..579b687917389fa4697c061e88f959b5dbdfa1af 100644 (file)
@@ -12,7 +12,6 @@ import {
 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
 import moment from 'moment';
 import { of } from 'rxjs';
-import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
 
 import { RbdService } from '~/app/shared/api/rbd.service';
 import { CdHelperClass } from '~/app/shared/classes/cd-helper.class';
@@ -79,8 +78,6 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
 
   modalRef: NgbModalRef;
 
-  peerConfigured = false;
-
   builders = {
     'rbd/snap/create': (metadata: any) => {
       const model = new RbdSnapshotModel();
@@ -95,7 +92,6 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
     private dimlessBinaryPipe: DimlessBinaryPipe,
     private cdDatePipe: CdDatePipe,
     private rbdService: RbdService,
-    private rbdMirrorService: RbdMirroringService,
     private taskManagerService: TaskManagerService,
     private notificationService: NotificationService,
     private summaryService: SummaryService,
@@ -148,20 +144,12 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
       }
     ];
 
-    this.rbdMirrorService.getPeerForPool(this.poolName).subscribe((resp: any) => {
-      if (resp.length > 0) {
-        this.peerConfigured = true;
-      }
-    });
-
     this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
     this.rbdTableActions = new RbdSnapshotActionsModel(
       this.actionLabels,
       this.featuresName,
       this.rbdService
     );
-    this.rbdTableActions.create.disable = () =>
-      !this.primary || (!this.peerConfigured && this.mirroring === 'snapshot');
     this.rbdTableActions.create.click = () => this.openCreateSnapshotModal();
     this.rbdTableActions.rename.click = () => this.openEditSnapshotModal();
     this.rbdTableActions.protect.click = () => this.toggleProtection();