]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
58d849c901ef57bad6a3abf55fcea7d329c812f2
[ceph.git] /
1 import {
2   Component,
3   Input,
4   OnChanges,
5   OnInit,
6   SimpleChanges,
7   TemplateRef,
8   ViewChild
9 } from '@angular/core';
10 import { BehaviorSubject, Observable, of } from 'rxjs';
11 import { catchError, switchMap, tap } from 'rxjs/operators';
12 import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.service';
13 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
14 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
15 import { Icons } from '~/app/shared/enum/icons.enum';
16 import { CdTableAction } from '~/app/shared/models/cd-table-action';
17 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
18 import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
19 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
20 import { CephfsSubvolume } from '~/app/shared/models/cephfs-subvolume.model';
21 import { ModalService } from '~/app/shared/services/modal.service';
22 import { CephfsSubvolumeFormComponent } from '../cephfs-subvolume-form/cephfs-subvolume-form.component';
23 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
24 import { Permissions } from '~/app/shared/models/permissions';
25 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
26 import { FinishedTask } from '~/app/shared/models/finished-task';
27 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
28 import { FormControl } from '@angular/forms';
29 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
30 import { CdForm } from '~/app/shared/forms/cd-form';
31 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
32 import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
33 import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
34 import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
35 import { HealthService } from '~/app/shared/api/health.service';
36
37 @Component({
38   selector: 'cd-cephfs-subvolume-list',
39   templateUrl: './cephfs-subvolume-list.component.html',
40   styleUrls: ['./cephfs-subvolume-list.component.scss']
41 })
42 export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnChanges {
43   @ViewChild('quotaUsageTpl', { static: true })
44   quotaUsageTpl: any;
45
46   @ViewChild('typeTpl', { static: true })
47   typeTpl: any;
48
49   @ViewChild('modeToHumanReadableTpl', { static: true })
50   modeToHumanReadableTpl: any;
51
52   @ViewChild('nameTpl', { static: true })
53   nameTpl: any;
54
55   @ViewChild('quotaSizeTpl', { static: true })
56   quotaSizeTpl: any;
57
58   @ViewChild('removeTmpl', { static: true })
59   removeTmpl: TemplateRef<any>;
60
61   @Input() fsName: string;
62   @Input() pools: any[];
63
64   columns: CdTableColumn[] = [];
65   tableActions: CdTableAction[];
66   context: CdTableFetchDataContext;
67   selection = new CdTableSelection();
68   removeForm: CdFormGroup;
69   icons = Icons;
70   permissions: Permissions;
71   modalRef: NgbModalRef;
72   errorMessage: string = '';
73   selectedName: string = '';
74
75   subVolumes$: Observable<CephfsSubvolume[]>;
76   subVolumeGroups$: Observable<CephfsSubvolumeGroup[]>;
77   subject = new BehaviorSubject<CephfsSubvolume[]>([]);
78   groupsSubject = new BehaviorSubject<CephfsSubvolume[]>([]);
79
80   subvolumeGroupList: string[] = [];
81   subVolumesList: CephfsSubvolume[] = [];
82
83   activeGroupName: string = '';
84
85   constructor(
86     private cephfsSubVolumeService: CephfsSubvolumeService,
87     private actionLabels: ActionLabelsI18n,
88     private modalService: ModalService,
89     private authStorageService: AuthStorageService,
90     private taskWrapper: TaskWrapperService,
91     private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService,
92     private healthService: HealthService
93   ) {
94     super();
95     this.permissions = this.authStorageService.getPermissions();
96   }
97
98   ngOnInit(): void {
99     this.columns = [
100       {
101         name: $localize`Name`,
102         prop: 'name',
103         flexGrow: 1,
104         cellTemplate: this.nameTpl
105       },
106       {
107         name: $localize`Data Pool`,
108         prop: 'info.data_pool',
109         flexGrow: 0.7,
110         cellTransformation: CellTemplate.badge,
111         customTemplateConfig: {
112           class: 'badge-background-primary'
113         }
114       },
115       {
116         name: $localize`Usage`,
117         prop: 'info.bytes_pcent',
118         flexGrow: 0.7,
119         cellTemplate: this.quotaUsageTpl,
120         cellClass: 'text-right'
121       },
122       {
123         name: $localize`Path`,
124         prop: 'info.path',
125         flexGrow: 1,
126         cellTransformation: CellTemplate.path
127       },
128       {
129         name: $localize`Mode`,
130         prop: 'info.mode',
131         flexGrow: 0.5,
132         cellTemplate: this.modeToHumanReadableTpl
133       },
134       {
135         name: $localize`Created`,
136         prop: 'info.created_at',
137         flexGrow: 0.5,
138         cellTransformation: CellTemplate.timeAgo
139       }
140     ];
141
142     this.tableActions = [
143       {
144         name: this.actionLabels.CREATE,
145         permission: 'create',
146         icon: Icons.add,
147         click: () => this.openModal()
148       },
149       {
150         name: this.actionLabels.EDIT,
151         permission: 'update',
152         icon: Icons.edit,
153         click: () => this.openModal(true)
154       },
155       {
156         name: this.actionLabels.ATTACH,
157         permission: 'read',
158         icon: Icons.bars,
159         disable: () => !this.selection?.hasSelection,
160         click: () => this.showAttachInfo()
161       },
162       {
163         name: this.actionLabels.REMOVE,
164         permission: 'delete',
165         icon: Icons.destroy,
166         click: () => this.removeSubVolumeModal()
167       }
168     ];
169
170     this.subVolumeGroups$ = this.groupsSubject.pipe(
171       switchMap(() =>
172         this.cephfsSubvolumeGroupService.get(this.fsName, false).pipe(
173           tap((groups) => {
174             this.subvolumeGroupList = groups.map((group) => group.name);
175             this.subvolumeGroupList.unshift('');
176           }),
177           catchError(() => {
178             this.context.error();
179             return of(null);
180           })
181         )
182       )
183     );
184   }
185
186   fetchData() {
187     this.subject.next([]);
188   }
189
190   ngOnChanges(changes: SimpleChanges) {
191     if (changes.fsName) {
192       this.subject.next([]);
193       this.groupsSubject.next([]);
194     }
195   }
196
197   updateSelection(selection: CdTableSelection) {
198     this.selection = selection;
199   }
200
201   showAttachInfo() {
202     const selectedSubVolume = this.selection?.selected?.[0];
203
204     this.healthService.getClusterFsid().subscribe({
205       next: (clusterId: string) => {
206         this.modalRef = this.modalService.show(CephfsMountDetailsComponent, {
207           onSubmit: () => this.modalRef.close(),
208           mountData: {
209             fsId: clusterId,
210             fsName: this.fsName,
211             rootPath: selectedSubVolume.info.path
212           }
213         });
214       }
215     });
216   }
217
218   openModal(edit = false) {
219     this.modalService.show(
220       CephfsSubvolumeFormComponent,
221       {
222         fsName: this.fsName,
223         subVolumeName: this.selection?.first()?.name,
224         subVolumeGroupName: this.activeGroupName,
225         pools: this.pools,
226         isEdit: edit
227       },
228       { size: 'lg' }
229     );
230   }
231
232   removeSubVolumeModal() {
233     this.removeForm = new CdFormGroup({
234       retainSnapshots: new FormControl(false)
235     });
236     this.errorMessage = '';
237     this.selectedName = this.selection.first().name;
238     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
239       actionDescription: 'Remove',
240       itemNames: [this.selectedName],
241       itemDescription: 'Subvolume',
242       childFormGroup: this.removeForm,
243       childFormGroupTemplate: this.removeTmpl,
244       submitAction: () =>
245         this.taskWrapper
246           .wrapTaskAroundCall({
247             task: new FinishedTask('cephfs/subvolume/remove', { subVolumeName: this.selectedName }),
248             call: this.cephfsSubVolumeService.remove(
249               this.fsName,
250               this.selectedName,
251               this.activeGroupName,
252               this.removeForm.getValue('retainSnapshots')
253             )
254           })
255           .subscribe({
256             complete: () => this.modalRef.close(),
257             error: (error) => {
258               this.modalRef.componentInstance.stopLoadingSpinner();
259               this.errorMessage = error.error.detail;
260             }
261           })
262     });
263   }
264
265   selectSubVolumeGroup(subVolumeGroupName: string) {
266     this.activeGroupName = subVolumeGroupName;
267     this.getSubVolumes();
268   }
269
270   getSubVolumes() {
271     this.subVolumes$ = this.subject.pipe(
272       switchMap(() =>
273         this.cephfsSubVolumeService.get(this.fsName, this.activeGroupName).pipe(
274           catchError(() => {
275             this.context?.error();
276             return of(null);
277           })
278         )
279       )
280     );
281   }
282 }