Skip to content

Commit bab43e8

Browse files
authored
Merge pull request ceph#55521 from ivoalmeida/snapshot-schedule-subvolume-copies
mgr/dashboard: snapshot schedule added subvolume and translations Reviewed-by: Ankush Behl <[email protected]> Reviewed-by: Nizamudeen A <[email protected]>
2 parents c8116c6 + 48e41aa commit bab43e8

File tree

10 files changed

+195
-40
lines changed

10 files changed

+195
-40
lines changed

src/pybind/mgr/dashboard/controllers/cephfs.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -969,13 +969,16 @@ def list(self, fs: str, path: str = '/', recursive: bool = True):
969969
)
970970
return json.loads(output_json)
971971

972-
def create(self, fs: str, path: str, snap_schedule: str, start: str, retention_policy=None):
972+
def create(self, fs: str, path: str, snap_schedule: str, start: str, retention_policy=None,
973+
subvol=None, group=None):
973974
error_code, _, err = mgr.remote('snap_schedule',
974975
'snap_schedule_add',
975976
path,
976977
snap_schedule,
977978
start,
978-
fs)
979+
fs,
980+
subvol,
981+
group)
979982

980983
if retention_policy:
981984
retention_policies = retention_policy.split('|')
@@ -999,7 +1002,8 @@ def create(self, fs: str, path: str, snap_schedule: str, start: str, retention_p
9991002

10001003
return f'Snapshot schedule for path {path} created successfully'
10011004

1002-
def set(self, fs: str, path: str, retention_to_add=None, retention_to_remove=None):
1005+
def set(self, fs: str, path: str, retention_to_add=None, retention_to_remove=None,
1006+
subvol=None, group=None):
10031007
def editRetentionPolicies(method, retention_policy):
10041008
if not retention_policy:
10051009
return
@@ -1014,8 +1018,8 @@ def editRetentionPolicies(method, retention_policy):
10141018
retention_spec_or_period,
10151019
retention_count,
10161020
fs,
1017-
None,
1018-
None)
1021+
subvol,
1022+
group)
10191023
if error_code_retention != 0:
10201024
raise DashboardException(
10211025
f'Failed to add/remove retention policy for path {path}: {err_retention}'
@@ -1027,15 +1031,16 @@ def editRetentionPolicies(method, retention_policy):
10271031
return f'Retention policies for snapshot schedule on path {path} updated successfully'
10281032

10291033
@RESTController.Resource('DELETE')
1030-
def delete_snapshot(self, fs: str, path: str, schedule: str, start: str):
1034+
def delete_snapshot(self, fs: str, path: str, schedule: str, start: str,
1035+
subvol=None, group=None):
10311036
error_code, _, err = mgr.remote('snap_schedule',
10321037
'snap_schedule_rm',
10331038
path,
10341039
schedule,
10351040
start,
10361041
fs,
1037-
None,
1038-
None)
1042+
subvol,
1043+
group)
10391044
if error_code != 0:
10401045
raise DashboardException(
10411046
f'Failed to delete snapshot schedule for path {path}: {err}'
@@ -1044,15 +1049,15 @@ def delete_snapshot(self, fs: str, path: str, schedule: str, start: str):
10441049
return f'Snapshot schedule for path {path} deleted successfully'
10451050

10461051
@RESTController.Resource('POST')
1047-
def deactivate(self, fs: str, path: str, schedule: str, start: str):
1052+
def deactivate(self, fs: str, path: str, schedule: str, start: str, subvol=None, group=None):
10481053
error_code, _, err = mgr.remote('snap_schedule',
10491054
'snap_schedule_deactivate',
10501055
path,
10511056
schedule,
10521057
start,
10531058
fs,
1054-
None,
1055-
None)
1059+
subvol,
1060+
group)
10561061
if error_code != 0:
10571062
raise DashboardException(
10581063
f'Failed to deactivate snapshot schedule for path {path}: {err}'
@@ -1061,15 +1066,15 @@ def deactivate(self, fs: str, path: str, schedule: str, start: str):
10611066
return f'Snapshot schedule for path {path} deactivated successfully'
10621067

10631068
@RESTController.Resource('POST')
1064-
def activate(self, fs: str, path: str, schedule: str, start: str):
1069+
def activate(self, fs: str, path: str, schedule: str, start: str, subvol=None, group=None):
10651070
error_code, _, err = mgr.remote('snap_schedule',
10661071
'snap_schedule_activate',
10671072
path,
10681073
schedule,
10691074
start,
10701075
fs,
1071-
None,
1072-
None)
1076+
subvol,
1077+
group)
10731078
if error_code != 0:
10741079
raise DashboardException(
10751080
f'Failed to activate snapshot schedule for path {path}: {err}'

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
i18n>A snapshot schedule for this path already exists.</span>
3737
</div>
3838
</div>
39+
3940
<!--Start date -->
4041
<div class="form-group row">
4142
<label class="cd-col-form-label required"

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { uniq } from 'lodash';
55
import { Observable, OperatorFunction, of, timer } from 'rxjs';
66
import { catchError, debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
77
import { CephfsSnapshotScheduleService } from '~/app/shared/api/cephfs-snapshot-schedule.service';
8+
import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.service';
89
import { DirectoryStoreService } from '~/app/shared/api/directory-store.service';
910
import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
1011
import { Icons } from '~/app/shared/enum/icons.enum';
@@ -23,6 +24,7 @@ import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
2324

2425
const VALIDATON_TIMER = 300;
2526
const DEBOUNCE_TIMER = 300;
27+
const DEFAULT_SUBVOLUME_GROUP = '_nogroup';
2628

2729
@Component({
2830
selector: 'cd-cephfs-snapshotschedule-form',
@@ -36,12 +38,18 @@ export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnIni
3638
retention!: string;
3739
start!: string;
3840
status!: string;
41+
subvol!: string;
42+
group!: string;
3943
id!: number;
4044
isEdit = false;
4145
icons = Icons;
4246
repeatFrequencies = Object.entries(RepeatFrequency);
4347
retentionFrequencies = Object.entries(RetentionFrequency);
4448
retentionPoliciesToRemove: RetentionPolicy[] = [];
49+
isDefaultSubvolumeGroup = false;
50+
subvolumeGroup!: string;
51+
subvolume!: string;
52+
isSubvolume = false;
4553

4654
currentTime!: NgbTimeStruct;
4755
minDate!: NgbDateStruct;
@@ -59,7 +67,8 @@ export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnIni
5967
private snapScheduleService: CephfsSnapshotScheduleService,
6068
private taskWrapper: TaskWrapperService,
6169
private cd: ChangeDetectorRef,
62-
public directoryStore: DirectoryStoreService
70+
public directoryStore: DirectoryStoreService,
71+
private subvolumeService: CephfsSubvolumeService
6372
) {
6473
super();
6574
this.resource = $localize`Snapshot schedule`;
@@ -82,6 +91,25 @@ export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnIni
8291
this.directoryStore.loadDirectories(this.id, '/', 3);
8392
this.createForm();
8493
this.isEdit ? this.populateForm() : this.loadingReady();
94+
this.snapScheduleForm.get('directory').valueChanges.subscribe({
95+
next: (value: string) => {
96+
this.subvolumeGroup = value?.split?.('/')?.[2];
97+
this.subvolume = value?.split?.('/')?.[3];
98+
this.subvolumeService
99+
.exists(
100+
this.subvolume,
101+
this.fsName,
102+
this.subvolumeGroup === DEFAULT_SUBVOLUME_GROUP ? '' : this.subvolumeGroup
103+
)
104+
.subscribe({
105+
next: (exists: boolean) => {
106+
this.isSubvolume = exists;
107+
this.isDefaultSubvolumeGroup =
108+
exists && this.subvolumeGroup === DEFAULT_SUBVOLUME_GROUP;
109+
}
110+
});
111+
}
112+
});
85113
}
86114

87115
get retentionPolicies() {
@@ -149,6 +177,7 @@ export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnIni
149177
this.snapScheduleForm = new CdFormGroup(
150178
{
151179
directory: new FormControl(undefined, {
180+
updateOn: 'blur',
152181
validators: [Validators.required]
153182
}),
154183
startDate: new FormControl(this.minDate, {
@@ -234,6 +263,8 @@ export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnIni
234263
const updateObj = {
235264
fs: this.fsName,
236265
path: this.path,
266+
subvol: this.subvol,
267+
group: this.group,
237268
retention_to_add: this.parseRetentionPolicies(retentionPoliciesToAdd) || null,
238269
retention_to_remove: this.parseRetentionPolicies(this.retentionPoliciesToRemove) || null
239270
};
@@ -262,9 +293,15 @@ export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnIni
262293
};
263294

264295
const retentionPoliciesValues = this.parseRetentionPolicies(values?.retentionPolicies);
265-
if (retentionPoliciesValues) {
266-
snapScheduleObj['retention_policy'] = retentionPoliciesValues;
296+
297+
if (retentionPoliciesValues) snapScheduleObj['retention_policy'] = retentionPoliciesValues;
298+
299+
if (this.isSubvolume) snapScheduleObj['subvol'] = this.subvolume;
300+
301+
if (this.isSubvolume && !this.isDefaultSubvolumeGroup) {
302+
snapScheduleObj['group'] = this.subvolumeGroup;
267303
}
304+
268305
this.taskWrapper
269306
.wrapTaskAroundCall({
270307
task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.CREATE, {

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.html

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
class="fw-bold"
2222
[ngbTooltip]="fullpathTpl"
2323
triggers="click:blur">
24-
{{ row.path | path }}
24+
{{ row.path?.split?.("@")?.[0] | path }}
2525
</span>
2626

2727
<span
2828
*ngIf="row.active; else inactiveStatusTpl">
2929
<i
3030
[ngClass]="[icons.success, icons.large]"
31-
ngbTooltip="{{ row.path }} is active"
31+
ngbTooltip="{{ row.path?.split?.('@')?.[0] }} is active"
3232
class="text-success"
3333
></i>
3434
</span>
@@ -37,7 +37,7 @@
3737
<i
3838
[ngClass]="[icons.warning, icons.large]"
3939
class="text-warning"
40-
ngbTooltip="{{ row.path }} has been deactivated"
40+
ngbTooltip="{{ row.path?.split?.('@')?.[0] }} has been deactivated"
4141
></i>
4242
</ng-template>
4343

@@ -46,10 +46,10 @@
4646
data-toggle="tooltip"
4747
[title]="row.path"
4848
class="font-monospace"
49-
>{{ row.path }}
49+
>{{ row.path?.split?.("@")?.[0] }}
5050
<cd-copy-2-clipboard-button
5151
*ngIf="row.path"
52-
[source]="row.path"
52+
[source]="row.path?.split?.('@')?.[0]"
5353
[byId]="false"
5454
[showIconOnly]="true"
5555
>
@@ -58,6 +58,26 @@
5858
</ng-template>
5959
</ng-template>
6060

61+
<ng-template
62+
#retentionTpl
63+
let-row="row">
64+
<ul *ngIf="row.retentionCopy.length; else noDataTpl">
65+
<li *ngFor="let ret of row.retentionCopy">{{ ret }}</li>
66+
</ul>
67+
</ng-template>
68+
69+
<ng-template
70+
#subvolTpl
71+
let-row="row">
72+
<span *ngIf="row.subvol; else noDataTpl">
73+
{{row.subvol}}
74+
</span>
75+
</ng-template>
76+
77+
78+
79+
<ng-template #noDataTpl>-</ng-template>
80+
6181
<cd-table
6282
[data]="snapshotSchedules$ | async"
6383
*ngIf="snapScheduleModuleStatus$ | async"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ul {
2+
list-style: none;
3+
padding: 0;
4+
}

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

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ export class CephfsSnapshotscheduleListComponent
4646
@ViewChild('pathTpl', { static: true })
4747
pathTpl: any;
4848

49+
@ViewChild('retentionTpl', { static: true })
50+
retentionTpl: any;
51+
52+
@ViewChild('subvolTpl', { static: true })
53+
subvolTpl: any;
54+
4955
@BlockUI()
5056
blockUI: NgBlockUI;
5157

@@ -122,7 +128,9 @@ export class CephfsSnapshotscheduleListComponent
122128
if (!status) {
123129
return of([]);
124130
}
125-
return this.snapshotScheduleService.getSnapshotScheduleList('/', this.fsName);
131+
return this.snapshotScheduleService
132+
.getSnapshotScheduleList('/', this.fsName)
133+
.pipe(map((list) => list.map((l) => ({ ...l, path: `${l.path}@${l.schedule}` }))));
126134
}),
127135
shareReplay(1)
128136
)
@@ -131,9 +139,11 @@ export class CephfsSnapshotscheduleListComponent
131139

132140
this.columns = [
133141
{ prop: 'path', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
134-
{ prop: 'subvol', name: $localize`Subvolume` },
135-
{ prop: 'schedule', name: $localize`Repeat interval` },
136-
{ prop: 'retention', name: $localize`Retention policy` },
142+
{ prop: 'subvol', name: $localize`Subvolume`, cellTemplate: this.subvolTpl },
143+
{ prop: 'scheduleCopy', name: $localize`Repeat interval` },
144+
{ prop: 'schedule', isHidden: true },
145+
{ prop: 'retentionCopy', name: $localize`Retention policy`, cellTemplate: this.retentionTpl },
146+
{ prop: 'retention', isHidden: true },
137147
{ prop: 'created_count', name: $localize`Created Count` },
138148
{ prop: 'pruned_count', name: $localize`Deleted Count` },
139149
{ prop: 'start', name: $localize`Start time`, cellTransformation: CellTemplate.timeAgo },
@@ -230,7 +240,7 @@ export class CephfsSnapshotscheduleListComponent
230240
}
231241

232242
deactivateSnapshotSchedule() {
233-
const { path, start, fs, schedule } = this.selection.first();
243+
const { path, start, fs, schedule, subvol, group } = this.selection.first();
234244

235245
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
236246
itemDescription: $localize`snapshot schedule`,
@@ -244,14 +254,16 @@ export class CephfsSnapshotscheduleListComponent
244254
path,
245255
schedule,
246256
start,
247-
fs
257+
fs,
258+
subvol,
259+
group
248260
})
249261
})
250262
});
251263
}
252264

253265
activateSnapshotSchedule() {
254-
const { path, start, fs, schedule } = this.selection.first();
266+
const { path, start, fs, schedule, subvol, group } = this.selection.first();
255267

256268
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
257269
itemDescription: $localize`snapshot schedule`,
@@ -265,14 +277,16 @@ export class CephfsSnapshotscheduleListComponent
265277
path,
266278
schedule,
267279
start,
268-
fs
280+
fs,
281+
subvol,
282+
group
269283
})
270284
})
271285
});
272286
}
273287

274288
deleteSnapshotSchedule() {
275-
const { path, start, fs, schedule } = this.selection.first();
289+
const { path, start, fs, schedule, subvol, group } = this.selection.first();
276290

277291
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
278292
itemDescription: $localize`snapshot schedule`,
@@ -285,7 +299,9 @@ export class CephfsSnapshotscheduleListComponent
285299
path,
286300
schedule,
287301
start,
288-
fs
302+
fs,
303+
subvol,
304+
group
289305
})
290306
})
291307
});

0 commit comments

Comments
 (0)