]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
026cf343240ef74e84b0deecf4d5cf7bbe412309
[ceph.git] /
1 import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
2
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as _ from 'lodash';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
6 import { Subscription } from 'rxjs';
7
8 import { IscsiService } from '../../../shared/api/iscsi.service';
9 import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
10 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
11 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
12 import { TableComponent } from '../../../shared/datatable/table/table.component';
13 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
14 import { Icons } from '../../../shared/enum/icons.enum';
15 import { CdTableAction } from '../../../shared/models/cd-table-action';
16 import { CdTableColumn } from '../../../shared/models/cd-table-column';
17 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
18 import { FinishedTask } from '../../../shared/models/finished-task';
19 import { Permission } from '../../../shared/models/permissions';
20 import { Task } from '../../../shared/models/task';
21 import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
22 import { NotAvailablePipe } from '../../../shared/pipes/not-available.pipe';
23 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
24 import { SummaryService } from '../../../shared/services/summary.service';
25 import { TaskListService } from '../../../shared/services/task-list.service';
26 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
27 import { IscsiTargetDiscoveryModalComponent } from '../iscsi-target-discovery-modal/iscsi-target-discovery-modal.component';
28
29 @Component({
30   selector: 'cd-iscsi-target-list',
31   templateUrl: './iscsi-target-list.component.html',
32   styleUrls: ['./iscsi-target-list.component.scss'],
33   providers: [TaskListService]
34 })
35 export class IscsiTargetListComponent extends ListWithDetails implements OnInit, OnDestroy {
36   @ViewChild(TableComponent, { static: false })
37   table: TableComponent;
38
39   available: boolean = undefined;
40   columns: CdTableColumn[];
41   docsUrl: string;
42   modalRef: BsModalRef;
43   permission: Permission;
44   selection = new CdTableSelection();
45   cephIscsiConfigVersion: number;
46   settings: any;
47   status: string;
48   summaryDataSubscription: Subscription;
49   tableActions: CdTableAction[];
50   targets: any[] = [];
51   icons = Icons;
52
53   builders = {
54     'iscsi/target/create': (metadata: object) => {
55       return {
56         target_iqn: metadata['target_iqn']
57       };
58     }
59   };
60
61   constructor(
62     private authStorageService: AuthStorageService,
63     private i18n: I18n,
64     private iscsiService: IscsiService,
65     private taskListService: TaskListService,
66     private cephReleaseNamePipe: CephReleaseNamePipe,
67     private notAvailablePipe: NotAvailablePipe,
68     private summaryservice: SummaryService,
69     private modalService: BsModalService,
70     private taskWrapper: TaskWrapperService,
71     public actionLabels: ActionLabelsI18n
72   ) {
73     super();
74     this.permission = this.authStorageService.getPermissions().iscsi;
75
76     this.tableActions = [
77       {
78         permission: 'create',
79         icon: Icons.add,
80         routerLink: () => '/block/iscsi/targets/create',
81         name: this.actionLabels.CREATE
82       },
83       {
84         permission: 'update',
85         icon: Icons.edit,
86         routerLink: () => `/block/iscsi/targets/edit/${this.selection.first().target_iqn}`,
87         name: this.actionLabels.EDIT,
88         disable: () => !this.selection.first() || !_.isUndefined(this.getDeleteDisableDesc()),
89         disableDesc: () => this.getEditDisableDesc()
90       },
91       {
92         permission: 'delete',
93         icon: Icons.destroy,
94         click: () => this.deleteIscsiTargetModal(),
95         name: this.actionLabels.DELETE,
96         disable: () => !this.selection.first() || !_.isUndefined(this.getDeleteDisableDesc()),
97         disableDesc: () => this.getDeleteDisableDesc()
98       }
99     ];
100   }
101
102   ngOnInit() {
103     this.columns = [
104       {
105         name: this.i18n('Target'),
106         prop: 'target_iqn',
107         flexGrow: 2,
108         cellTransformation: CellTemplate.executing
109       },
110       {
111         name: this.i18n('Portals'),
112         prop: 'cdPortals',
113         flexGrow: 2
114       },
115       {
116         name: this.i18n('Images'),
117         prop: 'cdImages',
118         flexGrow: 2
119       },
120       {
121         name: this.i18n('# Sessions'),
122         prop: 'info.num_sessions',
123         pipe: this.notAvailablePipe,
124         flexGrow: 1
125       }
126     ];
127
128     this.iscsiService.status().subscribe((result: any) => {
129       this.available = result.available;
130
131       if (result.available) {
132         this.iscsiService.version().subscribe((res: any) => {
133           this.cephIscsiConfigVersion = res['ceph_iscsi_config_version'];
134           this.taskListService.init(
135             () => this.iscsiService.listTargets(),
136             (resp) => this.prepareResponse(resp),
137             (targets) => (this.targets = targets),
138             () => this.onFetchError(),
139             this.taskFilter,
140             this.itemFilter,
141             this.builders
142           );
143         });
144
145         this.iscsiService.settings().subscribe((settings: any) => {
146           this.settings = settings;
147         });
148       } else {
149         const summary = this.summaryservice.getCurrentSummary();
150         const releaseName = this.cephReleaseNamePipe.transform(summary.version);
151         this.docsUrl = `http://docs.ceph.com/docs/${releaseName}/mgr/dashboard/#enabling-iscsi-management`;
152         this.status = result.message;
153       }
154     });
155   }
156
157   ngOnDestroy() {
158     if (this.summaryDataSubscription) {
159       this.summaryDataSubscription.unsubscribe();
160     }
161   }
162
163   getEditDisableDesc(): string | undefined {
164     const first = this.selection.first();
165     if (first && first.cdExecuting) {
166       return first.cdExecuting;
167     }
168     if (first && _.isUndefined(first['info'])) {
169       return this.i18n('Unavailable gateway(s)');
170     }
171
172     return undefined;
173   }
174
175   getDeleteDisableDesc(): string | undefined {
176     const first = this.selection.first();
177     if (first && first.cdExecuting) {
178       return first.cdExecuting;
179     }
180     if (first && _.isUndefined(first['info'])) {
181       return this.i18n('Unavailable gateway(s)');
182     }
183     if (first && first['info'] && first['info']['num_sessions']) {
184       return this.i18n('Target has active sessions');
185     }
186
187     return undefined;
188   }
189
190   prepareResponse(resp: any): any[] {
191     resp.forEach((element: Record<string, any>) => {
192       element.cdPortals = element.portals.map(
193         (portal: Record<string, any>) => `${portal.host}:${portal.ip}`
194       );
195       element.cdImages = element.disks.map(
196         (disk: Record<string, any>) => `${disk.pool}/${disk.image}`
197       );
198     });
199
200     return resp;
201   }
202
203   onFetchError() {
204     this.table.reset(); // Disable loading indicator.
205   }
206
207   itemFilter(entry: Record<string, any>, task: Task) {
208     return entry.target_iqn === task.metadata['target_iqn'];
209   }
210
211   taskFilter(task: Task) {
212     return ['iscsi/target/create', 'iscsi/target/edit', 'iscsi/target/delete'].includes(task.name);
213   }
214
215   updateSelection(selection: CdTableSelection) {
216     this.selection = selection;
217   }
218
219   deleteIscsiTargetModal() {
220     const target_iqn = this.selection.first().target_iqn;
221
222     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
223       initialState: {
224         itemDescription: this.i18n('iSCSI target'),
225         itemNames: [target_iqn],
226         submitActionObservable: () =>
227           this.taskWrapper.wrapTaskAroundCall({
228             task: new FinishedTask('iscsi/target/delete', {
229               target_iqn: target_iqn
230             }),
231             call: this.iscsiService.deleteTarget(target_iqn)
232           })
233       }
234     });
235   }
236
237   configureDiscoveryAuth() {
238     this.modalService.show(IscsiTargetDiscoveryModalComponent, {});
239   }
240 }