Skip to content

Commit 53d2daf

Browse files
authored
Merge pull request ceph#51590 from rhcs-dashboard/rbd-snapshot-disabled-fix
mgr/dashboard: fix create snapshot is disabled for rbd images Reviewed-by: Pegonzal <NOT@FOUND>
2 parents 02647cc + 896ae75 commit 53d2daf

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)