path,
retention_spec_or_period,
retention_count,
- fs)
+ fs,
+ subvol,
+ group)
if error_code_retention != 0:
raise DashboardException(
f'Failed to add retention policy for path {path}: {err_retention}'
@RESTController.Resource('DELETE')
def delete_snapshot(self, fs: str, path: str, schedule: str, start: str,
- subvol=None, group=None):
+ retention_policy=None, subvol=None, group=None):
+ if retention_policy:
+ # check if there are other snap schedules for this exact same path
+ error_code, out, err = mgr.remote('snap_schedule', 'snap_schedule_list',
+ path, False, fs, subvol, group, 'plain')
+
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to get snapshot schedule list for path {path}: {err}'
+ )
+ # only remove the retention policies if there no other snap schedules for this path
+ snapshot_schedule_list = out.split('\n')
+ if len(snapshot_schedule_list) <= 1:
+ retention_policies = retention_policy.split('|')
+ for retention in retention_policies:
+ retention_count = retention.split('-')[0]
+ retention_spec_or_period = retention.split('-')[1]
+ error_code, _, err = mgr.remote('snap_schedule',
+ 'snap_schedule_retention_rm',
+ path,
+ retention_spec_or_period,
+ retention_count,
+ fs,
+ subvol,
+ group)
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to remove retention policy for path {path}: {err}'
+ )
+ # remove snap schedule
error_code, _, err = mgr.remote('snap_schedule',
'snap_schedule_rm',
path,
}
deleteSnapshotSchedule() {
- const { path, start, fs, schedule, subvol, group } = this.selection.first();
+ const { path, start, fs, schedule, subvol, group, retention } = this.selection.first();
+ const retentionPolicy = retention
+ ?.split(/\s/gi)
+ ?.filter((r: string) => !!r)
+ ?.map((r: string) => {
+ const frequency = r.substring(r.length - 1);
+ const interval = r.substring(0, r.length - 1);
+ return `${interval}-${frequency}`;
+ })
+ ?.join('|')
+ ?.toLocaleLowerCase();
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
itemDescription: $localize`snapshot schedule`,
schedule,
start,
fs,
+ retentionPolicy,
subvol,
group
})
);
}
- delete({ fs, path, schedule, start, subvol, group }: Record<string, any>): Observable<any> {
+ delete({
+ fs,
+ path,
+ schedule,
+ start,
+ retentionPolicy,
+ subvol,
+ group
+ }: Record<string, any>): Observable<any> {
let deleteUrl = `${this.baseURL}/snapshot/schedule/${fs}/${encodeURIComponent(
path
)}/delete_snapshot?schedule=${schedule}&start=${encodeURIComponent(start)}`;
+ if (retentionPolicy) {
+ deleteUrl += `&retention_policy=${retentionPolicy}`;
+ }
if (subvol && group) {
deleteUrl += `&subvol=${encodeURIComponent(subvol)}&group=${encodeURIComponent(group)}`;
}
path: string,
fs: string,
retentionFrequencies: string[],
- retentionFrequenciesRemoved: string[] = []
+ retentionFrequenciesRemoved: string[] = [],
+ isSubvolume = false
): Observable<{ exists: boolean; errorIndex: number }> {
return this.getSnapshotSchedule(path, fs, false).pipe(
map((response) => {
let errorIndex = -1;
let exists = false;
- const index = response.findIndex((x) => x.path === path);
+ const index = response.findIndex((x) =>
+ isSubvolume ? x.path.startsWith(path) : x.path === path
+ );
const result = retentionFrequencies?.length
? intersection(
Object.keys(response?.[index]?.retention).filter(