]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.ts
import 15.2.5
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / iscsi-target-list / iscsi-target-list.component.ts
CommitLineData
11fdf7f2
TL
1import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
2
3import { I18n } from '@ngx-translate/i18n-polyfill';
92f5a8d4 4import * as _ from 'lodash';
11fdf7f2
TL
5import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
6import { Subscription } from 'rxjs';
7
8import { IscsiService } from '../../../shared/api/iscsi.service';
e306af50 9import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
11fdf7f2 10import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
eafe8130 11import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
11fdf7f2
TL
12import { TableComponent } from '../../../shared/datatable/table/table.component';
13import { CellTemplate } from '../../../shared/enum/cell-template.enum';
9f95a23c 14import { Icons } from '../../../shared/enum/icons.enum';
11fdf7f2
TL
15import { CdTableAction } from '../../../shared/models/cd-table-action';
16import { CdTableColumn } from '../../../shared/models/cd-table-column';
17import { CdTableSelection } from '../../../shared/models/cd-table-selection';
18import { FinishedTask } from '../../../shared/models/finished-task';
9f95a23c
TL
19import { Permission } from '../../../shared/models/permissions';
20import { Task } from '../../../shared/models/task';
92f5a8d4 21import { NotAvailablePipe } from '../../../shared/pipes/not-available.pipe';
11fdf7f2 22import { AuthStorageService } from '../../../shared/services/auth-storage.service';
11fdf7f2
TL
23import { TaskListService } from '../../../shared/services/task-list.service';
24import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
25import { IscsiTargetDiscoveryModalComponent } from '../iscsi-target-discovery-modal/iscsi-target-discovery-modal.component';
26
27@Component({
28 selector: 'cd-iscsi-target-list',
29 templateUrl: './iscsi-target-list.component.html',
30 styleUrls: ['./iscsi-target-list.component.scss'],
31 providers: [TaskListService]
32})
e306af50 33export class IscsiTargetListComponent extends ListWithDetails implements OnInit, OnDestroy {
9f95a23c 34 @ViewChild(TableComponent, { static: false })
11fdf7f2
TL
35 table: TableComponent;
36
37 available: boolean = undefined;
38 columns: CdTableColumn[];
11fdf7f2 39 modalRef: BsModalRef;
9f95a23c 40 permission: Permission;
11fdf7f2 41 selection = new CdTableSelection();
eafe8130 42 cephIscsiConfigVersion: number;
11fdf7f2
TL
43 settings: any;
44 status: string;
45 summaryDataSubscription: Subscription;
46 tableActions: CdTableAction[];
9f95a23c
TL
47 targets: any[] = [];
48 icons = Icons;
11fdf7f2
TL
49
50 builders = {
9f95a23c 51 'iscsi/target/create': (metadata: object) => {
11fdf7f2
TL
52 return {
53 target_iqn: metadata['target_iqn']
54 };
55 }
56 };
57
58 constructor(
59 private authStorageService: AuthStorageService,
60 private i18n: I18n,
61 private iscsiService: IscsiService,
62 private taskListService: TaskListService,
92f5a8d4 63 private notAvailablePipe: NotAvailablePipe,
11fdf7f2 64 private modalService: BsModalService,
eafe8130
TL
65 private taskWrapper: TaskWrapperService,
66 public actionLabels: ActionLabelsI18n
11fdf7f2 67 ) {
e306af50 68 super();
9f95a23c 69 this.permission = this.authStorageService.getPermissions().iscsi;
11fdf7f2
TL
70
71 this.tableActions = [
72 {
73 permission: 'create',
9f95a23c 74 icon: Icons.add,
eafe8130
TL
75 routerLink: () => '/block/iscsi/targets/create',
76 name: this.actionLabels.CREATE
11fdf7f2
TL
77 },
78 {
79 permission: 'update',
9f95a23c 80 icon: Icons.edit,
11fdf7f2 81 routerLink: () => `/block/iscsi/targets/edit/${this.selection.first().target_iqn}`,
92f5a8d4 82 name: this.actionLabels.EDIT,
f6b5b4d7 83 disable: () => !this.selection.first() || !_.isUndefined(this.getEditDisableDesc()),
92f5a8d4 84 disableDesc: () => this.getEditDisableDesc()
11fdf7f2
TL
85 },
86 {
87 permission: 'delete',
9f95a23c 88 icon: Icons.destroy,
11fdf7f2 89 click: () => this.deleteIscsiTargetModal(),
92f5a8d4
TL
90 name: this.actionLabels.DELETE,
91 disable: () => !this.selection.first() || !_.isUndefined(this.getDeleteDisableDesc()),
92 disableDesc: () => this.getDeleteDisableDesc()
11fdf7f2
TL
93 }
94 ];
95 }
96
97 ngOnInit() {
98 this.columns = [
99 {
100 name: this.i18n('Target'),
101 prop: 'target_iqn',
102 flexGrow: 2,
103 cellTransformation: CellTemplate.executing
104 },
105 {
106 name: this.i18n('Portals'),
107 prop: 'cdPortals',
108 flexGrow: 2
109 },
110 {
111 name: this.i18n('Images'),
112 prop: 'cdImages',
113 flexGrow: 2
114 },
115 {
116 name: this.i18n('# Sessions'),
117 prop: 'info.num_sessions',
92f5a8d4 118 pipe: this.notAvailablePipe,
11fdf7f2
TL
119 flexGrow: 1
120 }
121 ];
122
123 this.iscsiService.status().subscribe((result: any) => {
124 this.available = result.available;
125
126 if (result.available) {
eafe8130
TL
127 this.iscsiService.version().subscribe((res: any) => {
128 this.cephIscsiConfigVersion = res['ceph_iscsi_config_version'];
129 this.taskListService.init(
130 () => this.iscsiService.listTargets(),
131 (resp) => this.prepareResponse(resp),
132 (targets) => (this.targets = targets),
133 () => this.onFetchError(),
134 this.taskFilter,
135 this.itemFilter,
136 this.builders
137 );
138 });
11fdf7f2
TL
139
140 this.iscsiService.settings().subscribe((settings: any) => {
141 this.settings = settings;
142 });
143 } else {
11fdf7f2
TL
144 this.status = result.message;
145 }
146 });
147 }
148
149 ngOnDestroy() {
150 if (this.summaryDataSubscription) {
151 this.summaryDataSubscription.unsubscribe();
152 }
153 }
154
92f5a8d4
TL
155 getEditDisableDesc(): string | undefined {
156 const first = this.selection.first();
157 if (first && first.cdExecuting) {
158 return first.cdExecuting;
159 }
160 if (first && _.isUndefined(first['info'])) {
161 return this.i18n('Unavailable gateway(s)');
162 }
9f95a23c
TL
163
164 return undefined;
92f5a8d4
TL
165 }
166
167 getDeleteDisableDesc(): string | undefined {
168 const first = this.selection.first();
169 if (first && first.cdExecuting) {
170 return first.cdExecuting;
171 }
172 if (first && _.isUndefined(first['info'])) {
173 return this.i18n('Unavailable gateway(s)');
174 }
175 if (first && first['info'] && first['info']['num_sessions']) {
176 return this.i18n('Target has active sessions');
177 }
9f95a23c
TL
178
179 return undefined;
92f5a8d4
TL
180 }
181
11fdf7f2 182 prepareResponse(resp: any): any[] {
9f95a23c
TL
183 resp.forEach((element: Record<string, any>) => {
184 element.cdPortals = element.portals.map(
185 (portal: Record<string, any>) => `${portal.host}:${portal.ip}`
186 );
187 element.cdImages = element.disks.map(
188 (disk: Record<string, any>) => `${disk.pool}/${disk.image}`
189 );
11fdf7f2
TL
190 });
191
192 return resp;
193 }
194
195 onFetchError() {
196 this.table.reset(); // Disable loading indicator.
197 }
198
9f95a23c 199 itemFilter(entry: Record<string, any>, task: Task) {
11fdf7f2
TL
200 return entry.target_iqn === task.metadata['target_iqn'];
201 }
202
9f95a23c 203 taskFilter(task: Task) {
11fdf7f2
TL
204 return ['iscsi/target/create', 'iscsi/target/edit', 'iscsi/target/delete'].includes(task.name);
205 }
206
207 updateSelection(selection: CdTableSelection) {
208 this.selection = selection;
209 }
210
211 deleteIscsiTargetModal() {
212 const target_iqn = this.selection.first().target_iqn;
213
214 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
215 initialState: {
eafe8130
TL
216 itemDescription: this.i18n('iSCSI target'),
217 itemNames: [target_iqn],
11fdf7f2
TL
218 submitActionObservable: () =>
219 this.taskWrapper.wrapTaskAroundCall({
220 task: new FinishedTask('iscsi/target/delete', {
221 target_iqn: target_iqn
222 }),
223 call: this.iscsiService.deleteTarget(target_iqn)
224 })
225 }
226 });
227 }
228
229 configureDiscoveryAuth() {
230 this.modalService.show(IscsiTargetDiscoveryModalComponent, {});
231 }
232}