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