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