summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts')
-rw-r--r--src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts346
1 files changed, 346 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts
new file mode 100644
index 000000000..3840bb3fb
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts
@@ -0,0 +1,346 @@
+import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
+
+import {
+ ITreeOptions,
+ TreeComponent,
+ TreeModel,
+ TreeNode,
+ TREE_ACTIONS
+} from '@circlon/angular-tree-component';
+import _ from 'lodash';
+
+import { TableComponent } from '~/app/shared/datatable/table/table.component';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { CdTableColumn } from '~/app/shared/models/cd-table-column';
+import { BooleanTextPipe } from '~/app/shared/pipes/boolean-text.pipe';
+import { IscsiBackstorePipe } from '~/app/shared/pipes/iscsi-backstore.pipe';
+
+@Component({
+ selector: 'cd-iscsi-target-details',
+ templateUrl: './iscsi-target-details.component.html',
+ styleUrls: ['./iscsi-target-details.component.scss']
+})
+export class IscsiTargetDetailsComponent implements OnChanges, OnInit {
+ @Input()
+ selection: any;
+ @Input()
+ settings: any;
+ @Input()
+ cephIscsiConfigVersion: number;
+
+ @ViewChild('highlightTpl', { static: true })
+ highlightTpl: TemplateRef<any>;
+
+ private detailTable: TableComponent;
+ @ViewChild('detailTable')
+ set content(content: TableComponent) {
+ this.detailTable = content;
+ if (content) {
+ content.updateColumns();
+ }
+ }
+
+ @ViewChild('tree') tree: TreeComponent;
+
+ icons = Icons;
+ columns: CdTableColumn[];
+ data: any;
+ metadata: any = {};
+ selectedItem: any;
+ title: string;
+
+ nodes: any[] = [];
+ treeOptions: ITreeOptions = {
+ useVirtualScroll: true,
+ actionMapping: {
+ mouse: {
+ click: this.onNodeSelected.bind(this)
+ }
+ }
+ };
+
+ constructor(
+ private iscsiBackstorePipe: IscsiBackstorePipe,
+ private booleanTextPipe: BooleanTextPipe
+ ) {}
+
+ ngOnInit() {
+ this.columns = [
+ {
+ prop: 'displayName',
+ name: $localize`Name`,
+ flexGrow: 1,
+ cellTemplate: this.highlightTpl
+ },
+ {
+ prop: 'current',
+ name: $localize`Current`,
+ flexGrow: 1,
+ cellTemplate: this.highlightTpl
+ },
+ {
+ prop: 'default',
+ name: $localize`Default`,
+ flexGrow: 1,
+ cellTemplate: this.highlightTpl
+ }
+ ];
+ }
+
+ ngOnChanges() {
+ if (this.selection) {
+ this.selectedItem = this.selection;
+ this.generateTree();
+ }
+
+ this.data = undefined;
+ }
+
+ private generateTree() {
+ const target_meta = _.cloneDeep(this.selectedItem.target_controls);
+ // Target level authentication was introduced in ceph-iscsi config v11
+ if (this.cephIscsiConfigVersion > 10) {
+ _.extend(target_meta, _.cloneDeep(this.selectedItem.auth));
+ }
+ this.metadata = { root: target_meta };
+ const cssClasses = {
+ target: {
+ expanded: _.join(
+ this.selectedItem.cdExecuting
+ ? [Icons.large, Icons.spinner, Icons.spin]
+ : [Icons.large, Icons.bullseye],
+ ' '
+ )
+ },
+ initiators: {
+ expanded: _.join([Icons.large, Icons.user], ' '),
+ leaf: _.join([Icons.user], ' ')
+ },
+ groups: {
+ expanded: _.join([Icons.large, Icons.users], ' '),
+ leaf: _.join([Icons.users], ' ')
+ },
+ disks: {
+ expanded: _.join([Icons.large, Icons.disk], ' '),
+ leaf: _.join([Icons.disk], ' ')
+ },
+ portals: {
+ expanded: _.join([Icons.large, Icons.server], ' '),
+ leaf: _.join([Icons.server], ' ')
+ }
+ };
+
+ const disks: any[] = [];
+ _.forEach(this.selectedItem.disks, (disk) => {
+ const cdId = 'disk_' + disk.pool + '_' + disk.image;
+ this.metadata[cdId] = {
+ controls: disk.controls,
+ backstore: disk.backstore
+ };
+ ['wwn', 'lun'].forEach((k) => {
+ if (k in disk) {
+ this.metadata[cdId][k] = disk[k];
+ }
+ });
+ disks.push({
+ name: `${disk.pool}/${disk.image}`,
+ cdId: cdId,
+ cdIcon: cssClasses.disks.leaf
+ });
+ });
+
+ const portals: any[] = [];
+ _.forEach(this.selectedItem.portals, (portal) => {
+ portals.push({
+ name: `${portal.host}:${portal.ip}`,
+ cdIcon: cssClasses.portals.leaf
+ });
+ });
+
+ const clients: any[] = [];
+ _.forEach(this.selectedItem.clients, (client) => {
+ const client_metadata = _.cloneDeep(client.auth);
+ if (client.info) {
+ _.extend(client_metadata, client.info);
+ delete client_metadata['state'];
+ _.forEach(Object.keys(client.info.state), (state) => {
+ client_metadata[state.toLowerCase()] = client.info.state[state];
+ });
+ }
+ this.metadata['client_' + client.client_iqn] = client_metadata;
+
+ const luns: any[] = [];
+ client.luns.forEach((lun: Record<string, any>) => {
+ luns.push({
+ name: `${lun.pool}/${lun.image}`,
+ cdId: 'disk_' + lun.pool + '_' + lun.image,
+ cdIcon: cssClasses.disks.leaf
+ });
+ });
+
+ let status = '';
+ if (client.info) {
+ status = Object.keys(client.info.state).includes('LOGGED_IN') ? 'logged_in' : 'logged_out';
+ }
+ clients.push({
+ name: client.client_iqn,
+ status: status,
+ cdId: 'client_' + client.client_iqn,
+ children: luns,
+ cdIcon: cssClasses.initiators.leaf
+ });
+ });
+
+ const groups: any[] = [];
+ _.forEach(this.selectedItem.groups, (group) => {
+ const luns: any[] = [];
+ group.disks.forEach((disk: Record<string, any>) => {
+ luns.push({
+ name: `${disk.pool}/${disk.image}`,
+ cdId: 'disk_' + disk.pool + '_' + disk.image,
+ cdIcon: cssClasses.disks.leaf
+ });
+ });
+
+ const initiators: any[] = [];
+ group.members.forEach((member: string) => {
+ initiators.push({
+ name: member,
+ cdId: 'client_' + member
+ });
+ });
+
+ groups.push({
+ name: group.group_id,
+ cdIcon: cssClasses.groups.leaf,
+ children: [
+ {
+ name: 'Disks',
+ children: luns,
+ cdIcon: cssClasses.disks.expanded
+ },
+ {
+ name: 'Initiators',
+ children: initiators,
+ cdIcon: cssClasses.initiators.expanded
+ }
+ ]
+ });
+ });
+
+ this.nodes = [
+ {
+ name: this.selectedItem.target_iqn,
+ cdId: 'root',
+ isExpanded: true,
+ cdIcon: cssClasses.target.expanded,
+ children: [
+ {
+ name: 'Disks',
+ isExpanded: true,
+ children: disks,
+ cdIcon: cssClasses.disks.expanded
+ },
+ {
+ name: 'Portals',
+ isExpanded: true,
+ children: portals,
+ cdIcon: cssClasses.portals.expanded
+ },
+ {
+ name: 'Initiators',
+ isExpanded: true,
+ children: clients,
+ cdIcon: cssClasses.initiators.expanded
+ },
+ {
+ name: 'Groups',
+ isExpanded: true,
+ children: groups,
+ cdIcon: cssClasses.groups.expanded
+ }
+ ]
+ }
+ ];
+ }
+
+ private format(value: any) {
+ if (typeof value === 'boolean') {
+ return this.booleanTextPipe.transform(value);
+ }
+ return value;
+ }
+
+ onNodeSelected(tree: TreeModel, node: TreeNode) {
+ TREE_ACTIONS.ACTIVATE(tree, node, true);
+ if (node.data.cdId) {
+ this.title = node.data.name;
+ const tempData = this.metadata[node.data.cdId] || {};
+
+ if (node.data.cdId === 'root') {
+ this.detailTable?.toggleColumn({ prop: 'default', isHidden: true });
+ this.data = _.map(this.settings.target_default_controls, (value, key) => {
+ value = this.format(value);
+ return {
+ displayName: key,
+ default: value,
+ current: !_.isUndefined(tempData[key]) ? this.format(tempData[key]) : value
+ };
+ });
+ // Target level authentication was introduced in ceph-iscsi config v11
+ if (this.cephIscsiConfigVersion > 10) {
+ ['user', 'password', 'mutual_user', 'mutual_password'].forEach((key) => {
+ this.data.push({
+ displayName: key,
+ default: null,
+ current: tempData[key]
+ });
+ });
+ }
+ } else if (node.data.cdId.toString().startsWith('disk_')) {
+ this.detailTable?.toggleColumn({ prop: 'default', isHidden: true });
+ this.data = _.map(this.settings.disk_default_controls[tempData.backstore], (value, key) => {
+ value = this.format(value);
+ return {
+ displayName: key,
+ default: value,
+ current: !_.isUndefined(tempData.controls[key])
+ ? this.format(tempData.controls[key])
+ : value
+ };
+ });
+ this.data.push({
+ displayName: 'backstore',
+ default: this.iscsiBackstorePipe.transform(this.settings.default_backstore),
+ current: this.iscsiBackstorePipe.transform(tempData.backstore)
+ });
+ ['wwn', 'lun'].forEach((k) => {
+ if (k in tempData) {
+ this.data.push({
+ displayName: k,
+ default: undefined,
+ current: tempData[k]
+ });
+ }
+ });
+ } else {
+ this.detailTable?.toggleColumn({ prop: 'default', isHidden: false });
+ this.data = _.map(tempData, (value, key) => {
+ return {
+ displayName: key,
+ default: undefined,
+ current: this.format(value)
+ };
+ });
+ }
+ } else {
+ this.data = undefined;
+ }
+
+ this.detailTable?.updateColumns();
+ }
+
+ onUpdateData() {
+ this.tree.treeModel.expandAll();
+ }
+}