Skip to content

Commit 896ae75

Browse files
committed
mgr/dashboard: fix create snapshot is disabled for rbd images
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 <[email protected]>
1 parent d1b08f1 commit 896ae75

File tree

6 files changed

+53
-32
lines changed

6 files changed

+53
-32
lines changed

src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form-modal.component.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,32 @@
2424
autofocus>
2525
<span class="invalid-feedback"
2626
*ngIf="snapshotForm.showError('snapshotName', formDir, 'required')"
27-
i18n>This field is required.</span><br><br>
27+
i18n>This field is required.</span>
2828
<span *ngIf="((mirroring === 'snapshot') ? true : null) && (snapshotForm.getValue('mirrorImageSnapshot') === true) ? true: null"
2929
i18n>Snapshot mode is enabled on image <b>{{ imageName }}</b>: snapshot names are auto generated</span>
3030
</div>
3131
</div>
32-
<div *ngIf="(mirroring === 'snapshot') ? true : null">
33-
<div class="form-group row">
32+
<ng-container *ngIf="(mirroring === 'snapshot') ? true : null">
33+
<div class="form-group row"
34+
*ngIf="peerConfigured$ | async as peerConfigured">
3435
<div class="cd-col-form-offset">
3536
<div class="custom-control custom-checkbox">
3637
<input type="checkbox"
3738
class="custom-control-input"
3839
formControlName="mirrorImageSnapshot"
3940
name="mirrorImageSnapshot"
4041
id="mirrorImageSnapshot"
42+
[attr.disabled]="!(peerConfigured.length > 0) ? true : null"
4143
(change)="onMirrorCheckBoxChange()">
4244
<label for="mirrorImageSnapshot"
4345
class="custom-control-label"
4446
i18n>Mirror Image Snapshot</label>
47+
<cd-helper i18n
48+
*ngIf="!peerConfigured.length > 0">The peer must be registered to do this action.</cd-helper>
4549
</div>
4650
</div>
4751
</div>
48-
</div>
52+
</ng-container>
4953
</div>
5054
<div class="modal-footer">
5155
<cd-form-button-panel (submitActionEvent)="submit()"

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import { PipesModule } from '~/app/shared/pipes/pipes.module';
1111
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
1212
import { configureTestBed } from '~/testing/unit-test-helper';
1313
import { RbdSnapshotFormModalComponent } from './rbd-snapshot-form-modal.component';
14+
import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
15+
import { of } from 'rxjs';
1416

1517
describe('RbdSnapshotFormModalComponent', () => {
1618
let component: RbdSnapshotFormModalComponent;
1719
let fixture: ComponentFixture<RbdSnapshotFormModalComponent>;
20+
let rbdMirrorService: RbdMirroringService;
1821

1922
configureTestBed({
2023
imports: [
@@ -32,6 +35,7 @@ describe('RbdSnapshotFormModalComponent', () => {
3235
beforeEach(() => {
3336
fixture = TestBed.createComponent(RbdSnapshotFormModalComponent);
3437
component = fixture.componentInstance;
38+
rbdMirrorService = TestBed.inject(RbdMirroringService);
3539
});
3640

3741
it('should create', () => {
@@ -59,4 +63,22 @@ describe('RbdSnapshotFormModalComponent', () => {
5963
const button = fixture.debugElement.nativeElement.querySelector('cd-submit-button');
6064
expect(button.textContent).toBe('Rename RBD Snapshot');
6165
});
66+
67+
it('should enable the mirror image snapshot creation when peer is configured', () => {
68+
spyOn(rbdMirrorService, 'getPeerForPool').and.returnValue(of(['test_peer']));
69+
component.mirroring = 'snapshot';
70+
component.ngOnInit();
71+
fixture.detectChanges();
72+
const radio = fixture.debugElement.nativeElement.querySelector('#mirrorImageSnapshot');
73+
expect(radio.disabled).toBe(false);
74+
});
75+
76+
it('should disable the mirror image snapshot creation when peer is not configured', () => {
77+
spyOn(rbdMirrorService, 'getPeerForPool').and.returnValue(of([]));
78+
component.mirroring = 'snapshot';
79+
component.ngOnInit();
80+
fixture.detectChanges();
81+
const radio = fixture.debugElement.nativeElement.querySelector('#mirrorImageSnapshot');
82+
expect(radio.disabled).toBe(true);
83+
});
6284
});

src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form-modal.component.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { Component } from '@angular/core';
1+
import { Component, OnInit } from '@angular/core';
22
import { FormControl, Validators } from '@angular/forms';
33

44
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
5-
import { Subject } from 'rxjs';
5+
import { Observable, Subject } from 'rxjs';
6+
import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
67

78
import { RbdService } from '~/app/shared/api/rbd.service';
89
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
@@ -17,7 +18,7 @@ import { TaskManagerService } from '~/app/shared/services/task-manager.service';
1718
templateUrl: './rbd-snapshot-form-modal.component.html',
1819
styleUrls: ['./rbd-snapshot-form-modal.component.scss']
1920
})
20-
export class RbdSnapshotFormModalComponent {
21+
export class RbdSnapshotFormModalComponent implements OnInit {
2122
poolName: string;
2223
namespace: string;
2324
imageName: string;
@@ -32,12 +33,15 @@ export class RbdSnapshotFormModalComponent {
3233

3334
public onSubmit: Subject<string> = new Subject();
3435

36+
peerConfigured$: Observable<any>;
37+
3538
constructor(
3639
public activeModal: NgbActiveModal,
3740
private rbdService: RbdService,
3841
private taskManagerService: TaskManagerService,
3942
private notificationService: NotificationService,
40-
private actionLabels: ActionLabelsI18n
43+
private actionLabels: ActionLabelsI18n,
44+
private rbdMirrorService: RbdMirroringService
4145
) {
4246
this.action = this.actionLabels.CREATE;
4347
this.resource = $localize`RBD Snapshot`;
@@ -53,18 +57,22 @@ export class RbdSnapshotFormModalComponent {
5357
});
5458
}
5559

60+
ngOnInit(): void {
61+
this.peerConfigured$ = this.rbdMirrorService.getPeerForPool(this.poolName);
62+
}
63+
5664
setSnapName(snapName: string) {
5765
this.snapName = snapName;
58-
if (this.mirroring !== 'snapshot') {
59-
this.snapshotForm.get('snapshotName').setValue(snapName);
60-
} else {
61-
this.snapshotForm.get('snapshotName').clearValidators();
62-
}
66+
this.snapshotForm.get('snapshotName').setValue(snapName);
6367
}
6468

6569
onMirrorCheckBoxChange() {
6670
if (this.snapshotForm.getValue('mirrorImageSnapshot') === true) {
6771
this.snapshotForm.get('snapshotName').setValue('');
72+
this.snapshotForm.get('snapshotName').clearValidators();
73+
} else {
74+
this.snapshotForm.get('snapshotName').setValue(this.snapName);
75+
this.snapshotForm.get('snapshotName').setValidators([Validators.required]);
6876
}
6977
}
7078

src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ export class RbdSnapshotActionsModel {
3535
permission: 'update',
3636
icon: Icons.edit,
3737
name: actionLabels.RENAME,
38-
disable: (selection: CdTableSelection) => this.disableForMirrorSnapshot(selection)
38+
disable: (selection: CdTableSelection) =>
39+
this.disableForMirrorSnapshot(selection) || !selection.hasSingleSelection
3940
};
4041
this.protect = {
4142
permission: 'update',
@@ -76,7 +77,8 @@ export class RbdSnapshotActionsModel {
7677
permission: 'update',
7778
icon: Icons.undo,
7879
name: actionLabels.ROLLBACK,
79-
disable: (selection: CdTableSelection) => this.disableForMirrorSnapshot(selection)
80+
disable: (selection: CdTableSelection) =>
81+
this.disableForMirrorSnapshot(selection) || !selection.hasSingleSelection
8082
};
8183
this.deleteSnap = {
8284
permission: 'delete',

src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { NgbModalModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
77
import { MockComponent } from 'ng-mocks';
88
import { ToastrModule } from 'ngx-toastr';
99
import { Subject, throwError as observableThrowError } from 'rxjs';
10-
import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
1110

1211
import { RbdService } from '~/app/shared/api/rbd.service';
1312
import { ComponentsModule } from '~/app/shared/components/components.module';
@@ -86,7 +85,6 @@ describe('RbdSnapshotListComponent', () => {
8685
describe('api delete request', () => {
8786
let called: boolean;
8887
let rbdService: RbdService;
89-
let rbdMirroringService: RbdMirroringService;
9088
let notificationService: NotificationService;
9189
let authStorageService: AuthStorageService;
9290

@@ -95,7 +93,6 @@ describe('RbdSnapshotListComponent', () => {
9593
const modalService = TestBed.inject(ModalService);
9694
const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
9795
called = false;
98-
rbdMirroringService = new RbdMirroringService(null, null);
9996
rbdService = new RbdService(null, null);
10097
notificationService = new NotificationService(null, null, null);
10198
authStorageService = new AuthStorageService();
@@ -106,7 +103,6 @@ describe('RbdSnapshotListComponent', () => {
106103
null,
107104
null,
108105
rbdService,
109-
rbdMirroringService,
110106
null,
111107
notificationService,
112108
null,
@@ -205,7 +201,8 @@ describe('RbdSnapshotListComponent', () => {
205201
null,
206202
null,
207203
null,
208-
TestBed.inject(ActionLabelsI18n)
204+
TestBed.inject(ActionLabelsI18n),
205+
null
209206
);
210207
ref.componentInstance.onSubmit = new Subject();
211208
return ref;

src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
1313
import moment from 'moment';
1414
import { of } from 'rxjs';
15-
import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
1615

1716
import { RbdService } from '~/app/shared/api/rbd.service';
1817
import { CdHelperClass } from '~/app/shared/classes/cd-helper.class';
@@ -79,8 +78,6 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
7978

8079
modalRef: NgbModalRef;
8180

82-
peerConfigured = false;
83-
8481
builders = {
8582
'rbd/snap/create': (metadata: any) => {
8683
const model = new RbdSnapshotModel();
@@ -95,7 +92,6 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
9592
private dimlessBinaryPipe: DimlessBinaryPipe,
9693
private cdDatePipe: CdDatePipe,
9794
private rbdService: RbdService,
98-
private rbdMirrorService: RbdMirroringService,
9995
private taskManagerService: TaskManagerService,
10096
private notificationService: NotificationService,
10197
private summaryService: SummaryService,
@@ -148,20 +144,12 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
148144
}
149145
];
150146

151-
this.rbdMirrorService.getPeerForPool(this.poolName).subscribe((resp: any) => {
152-
if (resp.length > 0) {
153-
this.peerConfigured = true;
154-
}
155-
});
156-
157147
this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
158148
this.rbdTableActions = new RbdSnapshotActionsModel(
159149
this.actionLabels,
160150
this.featuresName,
161151
this.rbdService
162152
);
163-
this.rbdTableActions.create.disable = () =>
164-
!this.primary || (!this.peerConfigured && this.mirroring === 'snapshot');
165153
this.rbdTableActions.create.click = () => this.openCreateSnapshotModal();
166154
this.rbdTableActions.rename.click = () => this.openEditSnapshotModal();
167155
this.rbdTableActions.protect.click = () => this.toggleProtection();

0 commit comments

Comments
 (0)