Skip to content

Commit 6e42fb9

Browse files
authored
Merge pull request ceph#58286 from rhcs-dashboard/clone-validation
mgr/dashboard: fix clone async validators with different groups Reviewed-by: Ankush Behl <[email protected]>
2 parents dbc9816 + 9fbcf86 commit 6e42fb9

File tree

5 files changed

+85
-5
lines changed

5 files changed

+85
-5
lines changed

src/pybind/mgr/dashboard/frontend/cypress/e2e/common/global.feature.po.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,7 @@ And('I go to the {string} tab', (names: string) => {
3838
cy.contains('.nav.nav-tabs a', name).click();
3939
}
4040
});
41+
42+
And('I wait for {string} seconds', (seconds: number) => {
43+
cy.wait(seconds * 1000);
44+
});

src/pybind/mgr/dashboard/frontend/cypress/e2e/filesystems/snapshots.e2e-spec.feature

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Feature: CephFS Snapshot Management
22

3-
Goal: To test out the CephFS snapshot management features
3+
Goal: To test out the CephFS snapshot and clone management features
44

55
Background: Login
66
Given I am logged in
@@ -33,6 +33,48 @@ Feature: CephFS Snapshot Management
3333
And I go to the "Snapshots" tab
3434
Then I should see a table in the expanded row
3535

36+
Scenario: Create a CephFS Subvolume Snapshot
37+
Given I am on the "cephfs" page
38+
When I expand the row "test_cephfs"
39+
And I go to the "Snapshots" tab
40+
And I click on "Create" button from the expanded row
41+
And enter "snapshotName" "test_snapshot" in the modal
42+
And I click on "Create Snapshot" button
43+
Then I should see a row with "test_snapshot" in the expanded row
44+
45+
Scenario: Create a CephFS Subvolume Snapshot Clone
46+
Given I am on the "cephfs" page
47+
When I expand the row "test_cephfs"
48+
And I go to the "Snapshots" tab
49+
And I select a row "test_snapshot" in the expanded row
50+
And I click on "Clone" button from the table actions in the expanded row
51+
And enter "cloneName" "test_clone" in the modal
52+
And I click on "Create Clone" button
53+
Then I wait for "5" seconds
54+
And I go to the "Subvolumes" tab
55+
Then I should see a row with "test_clone" in the expanded row
56+
57+
Scenario: Remove a CephFS Subvolume Snapshot Clone
58+
Given I am on the "cephfs" page
59+
When I expand the row "test_cephfs"
60+
And I go to the "Subvolumes" tab
61+
And I select a row "test_clone" in the expanded row
62+
And I click on "Remove" button from the table actions in the expanded row
63+
And I check the tick box in modal
64+
And I click on "Remove Subvolume" button
65+
Then I wait for "5" seconds
66+
And I should not see a row with "test_clone" in the expanded row
67+
68+
Scenario: Remove a CephFS Subvolume Snapshot
69+
Given I am on the "cephfs" page
70+
When I expand the row "test_cephfs"
71+
And I go to the "Snapshots" tab
72+
And I select a row "test_snapshot" in the expanded row
73+
And I click on "Remove" button from the table actions in the expanded row
74+
And I check the tick box in modal
75+
And I click on "Remove Snapshot" button
76+
Then I should not see a row with "test_snapshot" in the expanded row
77+
3678
Scenario: Remove a CephFS Subvolume
3779
Given I am on the "cephfs" page
3880
When I expand the row "test_cephfs"

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges
113113
click: () => this.cloneModal()
114114
},
115115
{
116-
name: this.actionLabels.DELETE,
116+
name: this.actionLabels.REMOVE,
117117
permission: 'delete',
118118
icon: Icons.destroy,
119119
disable: () => !this.selection.hasSingleSelection,
@@ -224,7 +224,7 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges
224224
const subVolumeGroupName = this.activeGroupName;
225225
const fsName = this.fsName;
226226
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
227-
actionDescription: 'Delete',
227+
actionDescription: this.actionLabels.REMOVE,
228228
itemNames: [snapshotName],
229229
itemDescription: 'Snapshot',
230230
submitAction: () =>
@@ -270,7 +270,8 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges
270270
this.cephfsSubvolumeService,
271271
null,
272272
null,
273-
this.fsName
273+
this.fsName,
274+
this.activeGroupName
274275
)
275276
],
276277
required: true,
@@ -284,12 +285,23 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges
284285
name: 'groupName',
285286
value: this.activeGroupName,
286287
label: $localize`Group name`,
288+
valueChangeListener: true,
289+
dependsOn: 'cloneName',
287290
typeConfig: {
288291
options: allGroups
289292
}
290293
}
291294
],
292295
submitButtonText: $localize`Create Clone`,
296+
updateAsyncValidators: (value: any) =>
297+
CdValidators.unique(
298+
this.cephfsSubvolumeService.exists,
299+
this.cephfsSubvolumeService,
300+
null,
301+
null,
302+
this.fsName,
303+
value
304+
),
293305
onSubmit: (value: any) => {
294306
this.cephfsSubvolumeService
295307
.createSnapshotClone(

src/pybind/mgr/dashboard/frontend/src/app/shared/components/form-modal/form-modal.component.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export class FormModalComponent implements OnInit {
2323
submitButtonText: string;
2424
onSubmit: Function;
2525

26+
updateAsyncValidators?: Function;
27+
2628
// Internal
2729
formGroup: CdFormGroup;
2830

@@ -57,13 +59,22 @@ export class FormModalComponent implements OnInit {
5759
if (field.asyncValidators) {
5860
asyncValidators = asyncValidators.concat(field.asyncValidators);
5961
}
60-
return new UntypedFormControl(
62+
63+
const control = new UntypedFormControl(
6164
_.defaultTo(
6265
field.type === 'binary' ? this.dimlessBinaryPipe.transform(field.value) : field.value,
6366
null
6467
),
6568
{ validators, asyncValidators }
6669
);
70+
71+
if (field.valueChangeListener) {
72+
control.valueChanges.subscribe((value) => {
73+
const validatorToUpdate = this.updateAsyncValidators(value);
74+
this.updateValidation(field.dependsOn, validatorToUpdate);
75+
});
76+
}
77+
return control;
6778
}
6879

6980
getError(field: CdFormModalFieldConfig): string {
@@ -114,4 +125,10 @@ export class FormModalComponent implements OnInit {
114125
this.onSubmit(values);
115126
}
116127
}
128+
129+
updateValidation(name?: string, validator?: AsyncValidatorFn[]) {
130+
const field = this.formGroup.get(name);
131+
field.setAsyncValidators(validator);
132+
field.updateValueAndValidity();
133+
}
117134
}

src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-form-modal-field-config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export class CdFormModalFieldConfig {
1313
validators: ValidatorFn[];
1414
asyncValidators?: AsyncValidatorFn[];
1515

16+
// Used when you want to dynamically update the
17+
// async validators based on the field value
18+
valueChangeListener?: boolean;
19+
dependsOn?: string;
20+
1621
// --- Specific field properties ---
1722
typeConfig?: {
1823
[prop: string]: any;

0 commit comments

Comments
 (0)