1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
3 import { Node } from 'carbon-components-angular/treeview/tree-node.types';
4 import _ from 'lodash';
5 import { USER } from '~/app/shared/constants/app.constants';
7 import { TableComponent } from '~/app/shared/datatable/table/table.component';
8 import { Icons } from '~/app/shared/enum/icons.enum';
9 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
10 import { BooleanTextPipe } from '~/app/shared/pipes/boolean-text.pipe';
11 import { IscsiBackstorePipe } from '~/app/shared/pipes/iscsi-backstore.pipe';
12 import { TreeViewService } from '~/app/shared/services/tree-view.service';
15 selector: 'cd-iscsi-target-details',
16 templateUrl: './iscsi-target-details.component.html',
17 styleUrls: ['./iscsi-target-details.component.scss']
19 export class IscsiTargetDetailsComponent implements OnChanges, OnInit {
25 cephIscsiConfigVersion: number;
27 @ViewChild('highlightTpl', { static: true })
28 highlightTpl: TemplateRef<any>;
30 private detailTable: TableComponent;
31 @ViewChild('detailTable')
32 set content(content: TableComponent) {
33 this.detailTable = content;
35 content.updateColumns();
39 @ViewChild('treeNodeTemplate', { static: true }) labelTpl: TemplateRef<any>;
42 columns: CdTableColumn[];
51 private iscsiBackstorePipe: IscsiBackstorePipe,
52 private booleanTextPipe: BooleanTextPipe,
53 public treeViewService: TreeViewService
60 name: $localize`Name`,
62 cellTemplate: this.highlightTpl
66 name: $localize`Current`,
68 cellTemplate: this.highlightTpl
72 name: $localize`Default`,
74 cellTemplate: this.highlightTpl
81 this.selectedItem = this.selection;
85 this.data = undefined;
88 private generateTree() {
89 const target_meta = _.cloneDeep(this.selectedItem.target_controls);
90 // Target level authentication was introduced in ceph-iscsi config v11
91 if (this.cephIscsiConfigVersion > 10) {
92 _.extend(target_meta, _.cloneDeep(this.selectedItem.auth));
94 this.metadata = { root: target_meta };
98 this.selectedItem.cdExecuting
99 ? [Icons.large, Icons.spinner, Icons.spin]
100 : [Icons.large, Icons.bullseye],
105 expanded: _.join([Icons.large, Icons.user], ' '),
106 leaf: _.join([Icons.user], ' ')
109 expanded: _.join([Icons.large, Icons.users], ' '),
110 leaf: _.join([Icons.users], ' ')
113 expanded: _.join([Icons.large, Icons.disk], ' '),
114 leaf: _.join([Icons.disk], ' ')
117 expanded: _.join([Icons.large, Icons.server], ' '),
118 leaf: _.join([Icons.server], ' ')
122 const disks: any[] = [];
123 _.forEach(this.selectedItem.disks, (disk) => {
124 const id = 'disk_' + disk.pool + '_' + disk.image;
125 this.metadata[id] = {
126 controls: disk.controls,
127 backstore: disk.backstore
129 ['wwn', 'lun'].forEach((k) => {
131 this.metadata[id][k] = disk[k];
136 name: `${disk.pool}/${disk.image}`,
137 label: `${disk.pool}/${disk.image}`,
138 value: { cdIcon: cssClasses.disks.leaf }
142 const portals: Node[] = [];
143 _.forEach(this.selectedItem.portals, (portal) => {
145 label: this.labelTpl,
147 name: `${portal.host}:${portal.ip}`,
148 cdIcon: cssClasses.portals.leaf
151 name: `${portal.host}:${portal.ip}`,
152 cdIcon: cssClasses.portals.leaf
157 const clients: Node[] = [];
158 _.forEach(this.selectedItem.clients, (client: Node) => {
159 const client_metadata = _.cloneDeep(client.auth);
161 _.extend(client_metadata, client.info);
162 delete client_metadata['state'];
163 _.forEach(Object.keys(client.info.state), (state) => {
164 client_metadata[state.toLowerCase()] = client.info.state[state];
167 this.metadata['client_' + client.client_iqn] = client_metadata;
169 const luns: Node[] = [];
170 client.luns.forEach((lun: Node) => {
172 label: this.labelTpl,
174 name: `${lun.pool}/${lun.image}`,
175 cdIcon: cssClasses.disks.leaf
178 name: `${lun.pool}/${lun.image}`,
179 cdIcon: cssClasses.disks.leaf
181 id: 'disk_' + lun.pool + '_' + lun.image
187 status = Object.keys(client.info.state).includes('LOGGED_IN') ? 'logged_in' : 'logged_out';
190 label: this.labelTpl,
192 name: client.client_iqn,
194 cdIcon: cssClasses.initiators.leaf
197 name: client.client_iqn,
199 cdIcon: cssClasses.initiators.leaf
201 id: 'client_' + client.client_iqn,
206 const groups: Node[] = [];
207 _.forEach(this.selectedItem.groups, (group: Node) => {
208 const luns: Node[] = [];
209 group.disks.forEach((disk: Node) => {
211 label: this.labelTpl,
213 name: `${disk.pool}/${disk.image}`,
214 cdIcon: cssClasses.disks.leaf
217 name: `${disk.pool}/${disk.image}`,
218 cdIcon: cssClasses.disks.leaf
220 id: 'disk_' + disk.pool + '_' + disk.image
224 const initiators: Node[] = [];
225 group.members.forEach((member: string) => {
227 label: this.labelTpl,
228 labelContext: { name: member },
229 value: { name: member },
230 id: 'client_' + member
235 label: this.labelTpl,
236 labelContext: { name: group.group_id, cdIcon: cssClasses.groups.leaf },
237 value: { name: group.group_id, cdIcon: cssClasses.groups.leaf },
240 label: this.labelTpl,
241 labelContext: { name: 'Disks', cdIcon: cssClasses.disks.expanded },
242 value: { name: 'Disks', cdIcon: cssClasses.disks.expanded },
246 label: this.labelTpl,
247 labelContext: { name: 'Initiators', cdIcon: cssClasses.initiators.expanded },
248 value: { name: 'Initiators', cdIcon: cssClasses.initiators.expanded },
258 label: this.labelTpl,
260 name: this.selectedItem.target_iqn,
261 cdIcon: cssClasses.target.expanded
264 name: this.selectedItem.target_iqn,
265 cdIcon: cssClasses.target.expanded
270 label: this.labelTpl,
271 labelContext: { name: 'Disks', cdIcon: cssClasses.disks.expanded },
272 value: { name: 'Disks', cdIcon: cssClasses.disks.expanded },
277 label: this.labelTpl,
278 labelContext: { name: 'Portals', cdIcon: cssClasses.portals.expanded },
279 value: { name: 'Portals', cdIcon: cssClasses.portals.expanded },
284 label: this.labelTpl,
285 labelContext: { name: 'Initiators', cdIcon: cssClasses.initiators.expanded },
286 value: { name: 'Initiators', cdIcon: cssClasses.initiators.expanded },
291 label: this.labelTpl,
292 labelContext: { name: 'Groups', cdIcon: cssClasses.groups.expanded },
293 value: { name: 'Groups', cdIcon: cssClasses.groups.expanded },
302 private format(value: any) {
303 if (typeof value === 'boolean') {
304 return this.booleanTextPipe.transform(value);
309 onNodeSelected(node: Node) {
311 this.title = node?.value?.name;
312 const tempData = this.metadata[node.id] || {};
314 if (node.id === 'root') {
315 this.detailTable?.toggleColumn({ prop: 'default', isHidden: true });
316 this.data = _.map(this.settings.target_default_controls, (value, key) => {
317 value = this.format(value);
321 current: !_.isUndefined(tempData[key]) ? this.format(tempData[key]) : value
324 // Target level authentication was introduced in ceph-iscsi config v11
325 if (this.cephIscsiConfigVersion > 10) {
326 [USER, 'password', 'mutual_user', 'mutual_password'].forEach((key) => {
330 current: tempData[key]
334 } else if (node.id.toString().startsWith('disk_')) {
335 this.detailTable?.toggleColumn({ prop: 'default', isHidden: true });
336 this.data = _.map(this.settings.disk_default_controls[tempData.backstore], (value, key) => {
337 value = this.format(value);
341 current: !_.isUndefined(tempData.controls[key])
342 ? this.format(tempData.controls[key])
347 displayName: 'backstore',
348 default: this.iscsiBackstorePipe.transform(this.settings.default_backstore),
349 current: this.iscsiBackstorePipe.transform(tempData.backstore)
351 ['wwn', 'lun'].forEach((k) => {
361 this.detailTable?.toggleColumn({ prop: 'default', isHidden: false });
362 this.data = _.map(tempData, (value, key) => {
366 current: this.format(value)
371 this.data = undefined;
374 this.detailTable?.updateColumns();