]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts
import 15.2.4
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / iscsi-target-details / iscsi-target-details.component.ts
CommitLineData
11fdf7f2
TL
1import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
2
3import { I18n } from '@ngx-translate/i18n-polyfill';
9f95a23c
TL
4import {
5 ITreeOptions,
9f95a23c
TL
6 TreeComponent,
7 TreeModel,
e306af50
TL
8 TreeNode,
9 TREE_ACTIONS
9f95a23c 10} from 'angular-tree-component';
11fdf7f2 11import * as _ from 'lodash';
11fdf7f2
TL
12
13import { TableComponent } from '../../../shared/datatable/table/table.component';
9f95a23c 14import { Icons } from '../../../shared/enum/icons.enum';
11fdf7f2 15import { CdTableColumn } from '../../../shared/models/cd-table-column';
eafe8130 16import { BooleanTextPipe } from '../../../shared/pipes/boolean-text.pipe';
11fdf7f2
TL
17import { 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})
24export class IscsiTargetDetailsComponent implements OnChanges, OnInit {
25 @Input()
e306af50 26 selection: any;
11fdf7f2
TL
27 @Input()
28 settings: any;
eafe8130
TL
29 @Input()
30 cephIscsiConfigVersion: number;
11fdf7f2 31
9f95a23c 32 @ViewChild('highlightTpl', { static: true })
11fdf7f2
TL
33 highlightTpl: TemplateRef<any>;
34
35 private detailTable: TableComponent;
9f95a23c 36 @ViewChild('detailTable', { static: false })
11fdf7f2
TL
37 set content(content: TableComponent) {
38 this.detailTable = content;
39 if (content) {
40 content.updateColumns();
41 }
42 }
43
9f95a23c
TL
44 @ViewChild('tree', { static: false }) tree: TreeComponent;
45
46 icons = Icons;
11fdf7f2
TL
47 columns: CdTableColumn[];
48 data: any;
49 metadata: any = {};
50 selectedItem: any;
51 title: string;
9f95a23c
TL
52
53 nodes: any[] = [];
54 treeOptions: ITreeOptions = {
55 useVirtualScroll: true,
56 actionMapping: {
57 mouse: {
58 click: this.onNodeSelected.bind(this)
59 }
60 }
61 };
11fdf7f2 62
eafe8130
TL
63 constructor(
64 private i18n: I18n,
65 private iscsiBackstorePipe: IscsiBackstorePipe,
66 private booleanTextPipe: BooleanTextPipe
67 ) {}
11fdf7f2
TL
68
69 ngOnInit() {
70 this.columns = [
71 {
72 prop: 'displayName',
73 name: this.i18n('Name'),
eafe8130 74 flexGrow: 1,
11fdf7f2
TL
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() {
e306af50
TL
93 if (this.selection) {
94 this.selectedItem = this.selection;
11fdf7f2
TL
95 this.generateTree();
96 }
97
98 this.data = undefined;
99 }
100
101 private generateTree() {
eafe8130
TL
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 };
11fdf7f2
TL
108 const cssClasses = {
109 target: {
9f95a23c
TL
110 expanded: _.join(
111 this.selectedItem.cdExecuting
112 ? [Icons.large, Icons.spinner, Icons.spin]
113 : [Icons.large, Icons.bullseye],
114 ' '
115 )
11fdf7f2
TL
116 },
117 initiators: {
9f95a23c
TL
118 expanded: _.join([Icons.large, Icons.user], ' '),
119 leaf: _.join([Icons.user], ' ')
11fdf7f2
TL
120 },
121 groups: {
9f95a23c
TL
122 expanded: _.join([Icons.large, Icons.users], ' '),
123 leaf: _.join([Icons.users], ' ')
11fdf7f2
TL
124 },
125 disks: {
9f95a23c
TL
126 expanded: _.join([Icons.large, Icons.disk], ' '),
127 leaf: _.join([Icons.disk], ' ')
11fdf7f2
TL
128 },
129 portals: {
9f95a23c
TL
130 expanded: _.join([Icons.large, Icons.server], ' '),
131 leaf: _.join([Icons.server], ' ')
11fdf7f2
TL
132 }
133 };
134
9f95a23c 135 const disks: any[] = [];
11fdf7f2 136 _.forEach(this.selectedItem.disks, (disk) => {
9f95a23c
TL
137 const cdId = 'disk_' + disk.pool + '_' + disk.image;
138 this.metadata[cdId] = {
11fdf7f2
TL
139 controls: disk.controls,
140 backstore: disk.backstore
141 };
eafe8130
TL
142 ['wwn', 'lun'].forEach((k) => {
143 if (k in disk) {
9f95a23c 144 this.metadata[cdId][k] = disk[k];
eafe8130
TL
145 }
146 });
11fdf7f2 147 disks.push({
9f95a23c
TL
148 name: `${disk.pool}/${disk.image}`,
149 cdId: cdId,
150 cdIcon: cssClasses.disks.leaf
11fdf7f2
TL
151 });
152 });
153
9f95a23c 154 const portals: any[] = [];
11fdf7f2 155 _.forEach(this.selectedItem.portals, (portal) => {
9f95a23c
TL
156 portals.push({
157 name: `${portal.host}:${portal.ip}`,
158 cdIcon: cssClasses.portals.leaf
159 });
11fdf7f2
TL
160 });
161
9f95a23c 162 const clients: any[] = [];
11fdf7f2 163 _.forEach(this.selectedItem.clients, (client) => {
494da23a 164 const client_metadata = _.cloneDeep(client.auth);
eafe8130
TL
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 }
494da23a 172 this.metadata['client_' + client.client_iqn] = client_metadata;
11fdf7f2 173
9f95a23c
TL
174 const luns: any[] = [];
175 client.luns.forEach((lun: Record<string, any>) => {
11fdf7f2 176 luns.push({
9f95a23c
TL
177 name: `${lun.pool}/${lun.image}`,
178 cdId: 'disk_' + lun.pool + '_' + lun.image,
179 cdIcon: cssClasses.disks.leaf
11fdf7f2
TL
180 });
181 });
182
eafe8130
TL
183 let status = '';
184 if (client.info) {
185 status = Object.keys(client.info.state).includes('LOGGED_IN') ? 'logged_in' : 'logged_out';
186 }
11fdf7f2 187 clients.push({
9f95a23c 188 name: client.client_iqn,
eafe8130 189 status: status,
9f95a23c
TL
190 cdId: 'client_' + client.client_iqn,
191 children: luns,
192 cdIcon: cssClasses.initiators.leaf
11fdf7f2
TL
193 });
194 });
195
9f95a23c 196 const groups: any[] = [];
11fdf7f2 197 _.forEach(this.selectedItem.groups, (group) => {
9f95a23c
TL
198 const luns: any[] = [];
199 group.disks.forEach((disk: Record<string, any>) => {
11fdf7f2 200 luns.push({
9f95a23c
TL
201 name: `${disk.pool}/${disk.image}`,
202 cdId: 'disk_' + disk.pool + '_' + disk.image,
203 cdIcon: cssClasses.disks.leaf
11fdf7f2
TL
204 });
205 });
206
9f95a23c
TL
207 const initiators: any[] = [];
208 group.members.forEach((member: string) => {
11fdf7f2 209 initiators.push({
9f95a23c
TL
210 name: member,
211 cdId: 'client_' + member
11fdf7f2
TL
212 });
213 });
214
215 groups.push({
9f95a23c
TL
216 name: group.group_id,
217 cdIcon: cssClasses.groups.leaf,
11fdf7f2
TL
218 children: [
219 {
9f95a23c 220 name: 'Disks',
11fdf7f2 221 children: luns,
9f95a23c 222 cdIcon: cssClasses.disks.expanded
11fdf7f2
TL
223 },
224 {
9f95a23c 225 name: 'Initiators',
11fdf7f2 226 children: initiators,
9f95a23c 227 cdIcon: cssClasses.initiators.expanded
11fdf7f2
TL
228 }
229 ]
230 });
231 });
232
9f95a23c
TL
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
11fdf7f2 263 }
9f95a23c
TL
264 ]
265 }
266 ];
11fdf7f2
TL
267 }
268
9f95a23c 269 private format(value: any) {
eafe8130
TL
270 if (typeof value === 'boolean') {
271 return this.booleanTextPipe.transform(value);
272 }
273 return value;
274 }
275
9f95a23c
TL
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] || {};
11fdf7f2 281
9f95a23c 282 if (node.data.cdId === 'root') {
11fdf7f2
TL
283 this.columns[2].isHidden = false;
284 this.data = _.map(this.settings.target_default_controls, (value, key) => {
eafe8130 285 value = this.format(value);
11fdf7f2
TL
286 return {
287 displayName: key,
288 default: value,
eafe8130 289 current: !_.isUndefined(tempData[key]) ? this.format(tempData[key]) : value
11fdf7f2
TL
290 };
291 });
eafe8130
TL
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 }
9f95a23c 302 } else if (node.data.cdId.toString().startsWith('disk_')) {
11fdf7f2
TL
303 this.columns[2].isHidden = false;
304 this.data = _.map(this.settings.disk_default_controls[tempData.backstore], (value, key) => {
eafe8130 305 value = this.format(value);
11fdf7f2
TL
306 return {
307 displayName: key,
308 default: value,
eafe8130
TL
309 current: !_.isUndefined(tempData.controls[key])
310 ? this.format(tempData.controls[key])
311 : value
11fdf7f2
TL
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 });
eafe8130
TL
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 });
11fdf7f2
TL
328 } else {
329 this.columns[2].isHidden = true;
330 this.data = _.map(tempData, (value, key) => {
331 return {
332 displayName: key,
333 default: undefined,
eafe8130 334 current: this.format(value)
11fdf7f2
TL
335 };
336 });
337 }
338 } else {
339 this.data = undefined;
340 }
341
342 if (this.detailTable) {
343 this.detailTable.updateColumns();
344 }
345 }
9f95a23c
TL
346
347 onUpdateData() {
348 this.tree.treeModel.expandAll();
349 }
11fdf7f2 350}