1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as _ from 'lodash';
5 import { NodeEvent, TreeModel } from 'ng2-tree';
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';
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 {
21 selection: CdTableSelection;
25 cephIscsiConfigVersion: number;
27 @ViewChild('highlightTpl', { static: true })
28 highlightTpl: TemplateRef<any>;
30 private detailTable: TableComponent;
31 @ViewChild('detailTable', { static: false })
32 set content(content: TableComponent) {
33 this.detailTable = content;
35 content.updateColumns();
39 columns: CdTableColumn[];
48 private iscsiBackstorePipe: IscsiBackstorePipe,
49 private booleanTextPipe: BooleanTextPipe
56 name: this.i18n('Name'),
58 cellTemplate: this.highlightTpl
62 name: this.i18n('Current'),
64 cellTemplate: this.highlightTpl
68 name: this.i18n('Default'),
70 cellTemplate: this.highlightTpl
76 if (this.selection.hasSelection) {
77 this.selectedItem = this.selection.first();
81 this.data = undefined;
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));
90 this.metadata = { root: target_meta };
94 this.selectedItem.cdExecuting
95 ? [Icons.large, Icons.spinner, Icons.spin]
96 : [Icons.large, Icons.bullseye],
101 expanded: _.join([Icons.large, Icons.user], ' '),
102 leaf: _.join([Icons.user], ' ')
105 expanded: _.join([Icons.large, Icons.user], ' '),
106 leaf: _.join([Icons.user], ' ')
109 expanded: _.join([Icons.large, Icons.disk], ' '),
110 leaf: _.join([Icons.disk], ' ')
113 expanded: _.join([Icons.large, Icons.server], ' '),
114 leaf: _.join([Icons.large, Icons.server], ' ')
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
125 ['wwn', 'lun'].forEach((k) => {
127 this.metadata[id][k] = disk[k];
131 value: `${disk.pool}/${disk.image}`,
136 const portals: any[] = [];
137 _.forEach(this.selectedItem.portals, (portal) => {
138 portals.push({ value: `${portal.host}:${portal.ip}` });
141 const clients: any[] = [];
142 _.forEach(this.selectedItem.clients, (client) => {
143 const client_metadata = _.cloneDeep(client.auth);
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];
151 this.metadata['client_' + client.client_iqn] = client_metadata;
153 const luns: any[] = [];
154 client.luns.forEach((lun: Record<string, any>) => {
156 value: `${lun.pool}/${lun.image}`,
157 id: 'disk_' + lun.pool + '_' + lun.image,
159 cssClasses: cssClasses.disks
166 status = Object.keys(client.info.state).includes('LOGGED_IN') ? 'logged_in' : 'logged_out';
169 value: client.client_iqn,
171 id: 'client_' + client.client_iqn,
176 const groups: any[] = [];
177 _.forEach(this.selectedItem.groups, (group) => {
178 const luns: any[] = [];
179 group.disks.forEach((disk: Record<string, any>) => {
181 value: `${disk.pool}/${disk.image}`,
182 id: 'disk_' + disk.pool + '_' + disk.image
186 const initiators: any[] = [];
187 group.members.forEach((member: string) => {
190 id: 'client_' + member
195 value: group.group_id,
201 selectionAllowed: false,
202 cssClasses: cssClasses.disks
207 children: initiators,
209 selectionAllowed: false,
210 cssClasses: cssClasses.initiators
218 value: this.selectedItem.target_iqn,
222 cssClasses: cssClasses.target
229 selectionAllowed: false,
230 cssClasses: cssClasses.disks
237 selectionAllowed: false,
238 cssClasses: cssClasses.portals
245 selectionAllowed: false,
246 cssClasses: cssClasses.initiators
253 selectionAllowed: false,
254 cssClasses: cssClasses.groups
261 private format(value: any) {
262 if (typeof value === 'boolean') {
263 return this.booleanTextPipe.transform(value);
268 onNodeSelected(e: NodeEvent) {
270 this.title = e.node.value;
271 const tempData = this.metadata[e.node.id] || {};
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);
280 current: !_.isUndefined(tempData[key]) ? this.format(tempData[key]) : value
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) => {
289 current: tempData[key]
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);
300 current: !_.isUndefined(tempData.controls[key])
301 ? this.format(tempData.controls[key])
306 displayName: 'backstore',
307 default: this.iscsiBackstorePipe.transform(this.settings.default_backstore),
308 current: this.iscsiBackstorePipe.transform(tempData.backstore)
310 ['wwn', 'lun'].forEach((k) => {
320 this.columns[2].isHidden = true;
321 this.data = _.map(tempData, (value, key) => {
325 current: this.format(value)
330 this.data = undefined;
333 if (this.detailTable) {
334 this.detailTable.updateColumns();