1 import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
2 import { BehaviorSubject, Observable, forkJoin, of } from 'rxjs';
3 import { catchError, shareReplay, switchMap, tap } from 'rxjs/operators';
4 import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
5 import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.service';
6 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
7 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
8 import { Icons } from '~/app/shared/enum/icons.enum';
9 import { CdTableAction } from '~/app/shared/models/cd-table-action';
10 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
11 import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
12 import { CephfsSubvolume, SubvolumeSnapshot } from '~/app/shared/models/cephfs-subvolume.model';
13 import { CephfsSubvolumeSnapshotsFormComponent } from './cephfs-subvolume-snapshots-form/cephfs-subvolume-snapshots-form.component';
14 import { ModalService } from '~/app/shared/services/modal.service';
15 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
16 import { Permissions } from '~/app/shared/models/permissions';
17 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
18 import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe';
19 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
20 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
21 import { FinishedTask } from '~/app/shared/models/finished-task';
22 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
23 import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component';
24 import { NotificationService } from '~/app/shared/services/notification.service';
25 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
26 import moment from 'moment';
27 import { Validators } from '@angular/forms';
28 import { CdValidators } from '~/app/shared/forms/cd-validators';
29 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
32 selector: 'cd-cephfs-subvolume-snapshots-list',
33 templateUrl: './cephfs-subvolume-snapshots-list.component.html',
34 styleUrls: ['./cephfs-subvolume-snapshots-list.component.scss']
36 export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges {
37 @Input() fsName: string;
39 context: CdTableFetchDataContext;
40 columns: CdTableColumn[] = [];
41 tableActions: CdTableAction[];
42 selection = new CdTableSelection();
43 permissions: Permissions;
44 modalRef: NgbModalRef;
46 subVolumes$: Observable<CephfsSubvolume[]>;
47 snapshots$: Observable<any[]>;
48 snapshotSubject = new BehaviorSubject<SubvolumeSnapshot[]>([]);
49 subVolumeSubject = new BehaviorSubject<CephfsSubvolume[]>([]);
51 subvolumeGroupList: string[] = [];
52 subVolumesList: string[];
55 activeSubVolumeName = '';
57 isSubVolumesAvailable = false;
60 observables: any = [];
63 private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService,
64 private cephfsSubvolumeService: CephfsSubvolumeService,
65 private actionLabels: ActionLabelsI18n,
66 private modalService: ModalService,
67 private authStorageService: AuthStorageService,
68 private cdDatePipe: CdDatePipe,
69 private taskWrapper: TaskWrapperService,
70 private notificationService: NotificationService,
71 private cdsModalService: ModalCdsService
73 this.permissions = this.authStorageService.getPermissions();
79 name: $localize`Name`,
84 name: $localize`Created`,
85 prop: 'info.created_at',
90 name: $localize`Pending Clones`,
91 prop: 'info.has_pending_clones',
93 cellTransformation: CellTemplate.badge,
94 customTemplateConfig: {
96 no: { class: 'badge-success' },
97 yes: { class: 'badge-info' }
103 this.tableActions = [
105 name: this.actionLabels.CREATE,
106 permission: 'create',
108 click: () => this.openModal()
111 name: this.actionLabels.CLONE,
112 permission: 'create',
114 disable: () => !this.selection.hasSingleSelection,
115 click: () => this.cloneModal()
118 name: this.actionLabels.REMOVE,
119 permission: 'delete',
121 disable: () => !this.selection.hasSingleSelection,
122 click: () => this.deleteSnapshot()
126 this.cephfsSubvolumeGroupService
129 switchMap((groups) => {
130 // manually adding the group '_nogroup' to the list.
131 groups.unshift({ name: '' });
133 const observables = groups.map((group) =>
134 this.cephfsSubvolumeService.existsInFs(this.fsName, group.name).pipe(
135 switchMap((resp) => {
137 this.subvolumeGroupList.push(group.name);
139 return of(resp); // Emit the response
144 return forkJoin(observables);
148 if (this.subvolumeGroupList.length) {
149 this.isSubVolumesAvailable = true;
151 this.isLoading = false;
155 ngOnChanges(changes: SimpleChanges): void {
156 if (changes.fsName) {
157 this.subVolumeSubject.next([]);
161 selectSubVolumeGroup(subVolumeGroupName: string) {
162 this.activeGroupName = subVolumeGroupName;
163 this.getSubVolumes();
166 selectSubVolume(subVolumeName: string) {
167 this.activeSubVolumeName = subVolumeName;
168 this.getSubVolumesSnapshot();
172 this.subVolumes$ = this.subVolumeSubject.pipe(
174 this.cephfsSubvolumeService.get(this.fsName, this.activeGroupName, false).pipe(
176 this.subVolumesList = resp.map((subVolume) => subVolume.name);
177 this.activeSubVolumeName = resp[0].name;
178 this.getSubVolumesSnapshot();
185 getSubVolumesSnapshot() {
186 this.snapshots$ = this.snapshotSubject.pipe(
188 this.cephfsSubvolumeService
189 .getSnapshots(this.fsName, this.activeSubVolumeName, this.activeGroupName)
192 this.context.error();
202 this.snapshotSubject.next([]);
205 openModal(edit = false) {
206 this.modalService.show(
207 CephfsSubvolumeSnapshotsFormComponent,
210 subVolumeName: this.activeSubVolumeName,
211 subVolumeGroupName: this.activeGroupName,
212 subVolumeGroups: this.subvolumeGroupList,
219 updateSelection(selection: CdTableSelection) {
220 this.selection = selection;
224 const snapshotName = this.selection.first().name;
225 const subVolumeName = this.activeSubVolumeName;
226 const subVolumeGroupName = this.activeGroupName;
227 const fsName = this.fsName;
228 this.modalRef = this.cdsModalService.show(CriticalConfirmationModalComponent, {
229 actionDescription: this.actionLabels.REMOVE,
230 itemNames: [snapshotName],
231 itemDescription: 'Snapshot',
234 .wrapTaskAroundCall({
235 task: new FinishedTask('cephfs/subvolume/snapshot/delete', {
237 subVolumeName: subVolumeName,
238 subVolumeGroupName: subVolumeGroupName,
239 snapshotName: snapshotName
241 call: this.cephfsSubvolumeService.deleteSnapshot(
249 complete: () => this.cdsModalService.dismissAll(),
250 error: () => this.modalRef.componentInstance.stopLoadingSpinner()
256 const cloneName = `clone_${moment().toISOString(true)}`;
257 const allGroups = Array.from(this.subvolumeGroupList).map((group) => {
258 return { value: group, text: group === '' ? '_nogroup' : group };
260 this.modalService.show(FormModalComponent, {
261 titleText: $localize`Create clone`,
267 label: $localize`Name`,
268 validators: [Validators.required, Validators.pattern(/^[.A-Za-z0-9_+:-]+$/)],
271 this.cephfsSubvolumeService.exists,
272 this.cephfsSubvolumeService,
281 pattern: $localize`Allowed characters are letters, numbers, '.', '-', '+', ':' or '_'`,
282 notUnique: $localize`A subvolume or clone with this name already exists.`
288 value: this.activeGroupName,
289 label: $localize`Group name`,
290 valueChangeListener: true,
291 dependsOn: 'cloneName',
297 submitButtonText: $localize`Create Clone`,
298 updateAsyncValidators: (value: any) =>
300 this.cephfsSubvolumeService.exists,
301 this.cephfsSubvolumeService,
307 onSubmit: (value: any) => {
308 this.cephfsSubvolumeService
309 .createSnapshotClone(
311 this.activeSubVolumeName,
312 this.selection.first().name,
314 this.activeGroupName,
318 this.notificationService.show(
319 NotificationType.success,
320 $localize`Created Clone "${value.cloneName}" successfully.`