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