]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
929340a15b6ece7049b094a714ec701911320efd
[ceph.git] /
1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
2
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as _ from 'lodash';
5 import { NodeEvent, TreeModel } from 'ng2-tree';
6
7 import { TableComponent } from '../../../shared/datatable/table/table.component';
8 import { Icons } from '../../../shared/enum/icons.enum';
9 import { CdTableColumn } from '../../../shared/models/cd-table-column';
10 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
11 import { BooleanTextPipe } from '../../../shared/pipes/boolean-text.pipe';
12 import { IscsiBackstorePipe } from '../../../shared/pipes/iscsi-backstore.pipe';
13
14 @Component({
15   selector: 'cd-iscsi-target-details',
16   templateUrl: './iscsi-target-details.component.html',
17   styleUrls: ['./iscsi-target-details.component.scss']
18 })
19 export class IscsiTargetDetailsComponent implements OnChanges, OnInit {
20   @Input()
21   selection: CdTableSelection;
22   @Input()
23   settings: any;
24   @Input()
25   cephIscsiConfigVersion: number;
26
27   @ViewChild('highlightTpl', { static: true })
28   highlightTpl: TemplateRef<any>;
29
30   private detailTable: TableComponent;
31   @ViewChild('detailTable', { static: false })
32   set content(content: TableComponent) {
33     this.detailTable = content;
34     if (content) {
35       content.updateColumns();
36     }
37   }
38
39   columns: CdTableColumn[];
40   data: any;
41   metadata: any = {};
42   selectedItem: any;
43   title: string;
44   tree: TreeModel;
45
46   constructor(
47     private i18n: I18n,
48     private iscsiBackstorePipe: IscsiBackstorePipe,
49     private booleanTextPipe: BooleanTextPipe
50   ) {}
51
52   ngOnInit() {
53     this.columns = [
54       {
55         prop: 'displayName',
56         name: this.i18n('Name'),
57         flexGrow: 1,
58         cellTemplate: this.highlightTpl
59       },
60       {
61         prop: 'current',
62         name: this.i18n('Current'),
63         flexGrow: 1,
64         cellTemplate: this.highlightTpl
65       },
66       {
67         prop: 'default',
68         name: this.i18n('Default'),
69         flexGrow: 1,
70         cellTemplate: this.highlightTpl
71       }
72     ];
73   }
74
75   ngOnChanges() {
76     if (this.selection.hasSelection) {
77       this.selectedItem = this.selection.first();
78       this.generateTree();
79     }
80
81     this.data = undefined;
82   }
83
84   private generateTree() {
85     const target_meta = _.cloneDeep(this.selectedItem.target_controls);
86     // Target level authentication was introduced in ceph-iscsi config v11
87     if (this.cephIscsiConfigVersion > 10) {
88       _.extend(target_meta, _.cloneDeep(this.selectedItem.auth));
89     }
90     this.metadata = { root: target_meta };
91     const cssClasses = {
92       target: {
93         expanded: _.join(
94           this.selectedItem.cdExecuting
95             ? [Icons.large, Icons.spinner, Icons.spin]
96             : [Icons.large, Icons.bullseye],
97           ' '
98         )
99       },
100       initiators: {
101         expanded: _.join([Icons.large, Icons.user], ' '),
102         leaf: _.join([Icons.user], ' ')
103       },
104       groups: {
105         expanded: _.join([Icons.large, Icons.user], ' '),
106         leaf: _.join([Icons.user], ' ')
107       },
108       disks: {
109         expanded: _.join([Icons.large, Icons.disk], ' '),
110         leaf: _.join([Icons.disk], ' ')
111       },
112       portals: {
113         expanded: _.join([Icons.large, Icons.server], ' '),
114         leaf: _.join([Icons.large, Icons.server], ' ')
115       }
116     };
117
118     const disks: any[] = [];
119     _.forEach(this.selectedItem.disks, (disk) => {
120       const id = 'disk_' + disk.pool + '_' + disk.image;
121       this.metadata[id] = {
122         controls: disk.controls,
123         backstore: disk.backstore
124       };
125       ['wwn', 'lun'].forEach((k) => {
126         if (k in disk) {
127           this.metadata[id][k] = disk[k];
128         }
129       });
130       disks.push({
131         value: `${disk.pool}/${disk.image}`,
132         id: id
133       });
134     });
135
136     const portals: any[] = [];
137     _.forEach(this.selectedItem.portals, (portal) => {
138       portals.push({ value: `${portal.host}:${portal.ip}` });
139     });
140
141     const clients: any[] = [];
142     _.forEach(this.selectedItem.clients, (client) => {
143       const client_metadata = _.cloneDeep(client.auth);
144       if (client.info) {
145         _.extend(client_metadata, client.info);
146         delete client_metadata['state'];
147         _.forEach(Object.keys(client.info.state), (state) => {
148           client_metadata[state.toLowerCase()] = client.info.state[state];
149         });
150       }
151       this.metadata['client_' + client.client_iqn] = client_metadata;
152
153       const luns: any[] = [];
154       client.luns.forEach((lun: Record<string, any>) => {
155         luns.push({
156           value: `${lun.pool}/${lun.image}`,
157           id: 'disk_' + lun.pool + '_' + lun.image,
158           settings: {
159             cssClasses: cssClasses.disks
160           }
161         });
162       });
163
164       let status = '';
165       if (client.info) {
166         status = Object.keys(client.info.state).includes('LOGGED_IN') ? 'logged_in' : 'logged_out';
167       }
168       clients.push({
169         value: client.client_iqn,
170         status: status,
171         id: 'client_' + client.client_iqn,
172         children: luns
173       });
174     });
175
176     const groups: any[] = [];
177     _.forEach(this.selectedItem.groups, (group) => {
178       const luns: any[] = [];
179       group.disks.forEach((disk: Record<string, any>) => {
180         luns.push({
181           value: `${disk.pool}/${disk.image}`,
182           id: 'disk_' + disk.pool + '_' + disk.image
183         });
184       });
185
186       const initiators: any[] = [];
187       group.members.forEach((member: string) => {
188         initiators.push({
189           value: member,
190           id: 'client_' + member
191         });
192       });
193
194       groups.push({
195         value: group.group_id,
196         children: [
197           {
198             value: 'Disks',
199             children: luns,
200             settings: {
201               selectionAllowed: false,
202               cssClasses: cssClasses.disks
203             }
204           },
205           {
206             value: 'Initiators',
207             children: initiators,
208             settings: {
209               selectionAllowed: false,
210               cssClasses: cssClasses.initiators
211             }
212           }
213         ]
214       });
215     });
216
217     this.tree = {
218       value: this.selectedItem.target_iqn,
219       id: 'root',
220       settings: {
221         static: true,
222         cssClasses: cssClasses.target
223       },
224       children: [
225         {
226           value: 'Disks',
227           children: disks,
228           settings: {
229             selectionAllowed: false,
230             cssClasses: cssClasses.disks
231           }
232         },
233         {
234           value: 'Portals',
235           children: portals,
236           settings: {
237             selectionAllowed: false,
238             cssClasses: cssClasses.portals
239           }
240         },
241         {
242           value: 'Initiators',
243           children: clients,
244           settings: {
245             selectionAllowed: false,
246             cssClasses: cssClasses.initiators
247           }
248         },
249         {
250           value: 'Groups',
251           children: groups,
252           settings: {
253             selectionAllowed: false,
254             cssClasses: cssClasses.groups
255           }
256         }
257       ]
258     };
259   }
260
261   private format(value: any) {
262     if (typeof value === 'boolean') {
263       return this.booleanTextPipe.transform(value);
264     }
265     return value;
266   }
267
268   onNodeSelected(e: NodeEvent) {
269     if (e.node.id) {
270       this.title = e.node.value;
271       const tempData = this.metadata[e.node.id] || {};
272
273       if (e.node.id === 'root') {
274         this.columns[2].isHidden = false;
275         this.data = _.map(this.settings.target_default_controls, (value, key) => {
276           value = this.format(value);
277           return {
278             displayName: key,
279             default: value,
280             current: !_.isUndefined(tempData[key]) ? this.format(tempData[key]) : value
281           };
282         });
283         // Target level authentication was introduced in ceph-iscsi config v11
284         if (this.cephIscsiConfigVersion > 10) {
285           ['user', 'password', 'mutual_user', 'mutual_password'].forEach((key) => {
286             this.data.push({
287               displayName: key,
288               default: null,
289               current: tempData[key]
290             });
291           });
292         }
293       } else if (e.node.id.toString().startsWith('disk_')) {
294         this.columns[2].isHidden = false;
295         this.data = _.map(this.settings.disk_default_controls[tempData.backstore], (value, key) => {
296           value = this.format(value);
297           return {
298             displayName: key,
299             default: value,
300             current: !_.isUndefined(tempData.controls[key])
301               ? this.format(tempData.controls[key])
302               : value
303           };
304         });
305         this.data.push({
306           displayName: 'backstore',
307           default: this.iscsiBackstorePipe.transform(this.settings.default_backstore),
308           current: this.iscsiBackstorePipe.transform(tempData.backstore)
309         });
310         ['wwn', 'lun'].forEach((k) => {
311           if (k in tempData) {
312             this.data.push({
313               displayName: k,
314               default: undefined,
315               current: tempData[k]
316             });
317           }
318         });
319       } else {
320         this.columns[2].isHidden = true;
321         this.data = _.map(tempData, (value, key) => {
322           return {
323             displayName: key,
324             default: undefined,
325             current: this.format(value)
326           };
327         });
328       }
329     } else {
330       this.data = undefined;
331     }
332
333     if (this.detailTable) {
334       this.detailTable.updateColumns();
335     }
336   }
337 }