]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
75042749a92c255df5687364f1f3cb4efdba4eea
[ceph.git] /
1 import { Component, ViewChild } from '@angular/core';
2
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import { BlockUI, NgBlockUI } from 'ng-block-ui';
5 import { timer as observableTimer } from 'rxjs';
6
7 import { MgrModuleService } from '../../../../shared/api/mgr-module.service';
8 import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
9 import { TableComponent } from '../../../../shared/datatable/table/table.component';
10 import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
11 import { Icons } from '../../../../shared/enum/icons.enum';
12 import { CdTableAction } from '../../../../shared/models/cd-table-action';
13 import { CdTableColumn } from '../../../../shared/models/cd-table-column';
14 import { CdTableFetchDataContext } from '../../../../shared/models/cd-table-fetch-data-context';
15 import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
16 import { Permission } from '../../../../shared/models/permissions';
17 import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
18 import { NotificationService } from '../../../../shared/services/notification.service';
19
20 @Component({
21   selector: 'cd-mgr-module-list',
22   templateUrl: './mgr-module-list.component.html',
23   styleUrls: ['./mgr-module-list.component.scss']
24 })
25 export class MgrModuleListComponent extends ListWithDetails {
26   @ViewChild(TableComponent, { static: true })
27   table: TableComponent;
28   @BlockUI()
29   blockUI: NgBlockUI;
30
31   permission: Permission;
32   tableActions: CdTableAction[];
33   columns: CdTableColumn[] = [];
34   modules: object[] = [];
35   selection: CdTableSelection = new CdTableSelection();
36
37   constructor(
38     private authStorageService: AuthStorageService,
39     private mgrModuleService: MgrModuleService,
40     private notificationService: NotificationService,
41     private i18n: I18n
42   ) {
43     super();
44     this.permission = this.authStorageService.getPermissions().configOpt;
45     this.columns = [
46       {
47         name: this.i18n('Name'),
48         prop: 'name',
49         flexGrow: 1
50       },
51       {
52         name: this.i18n('Enabled'),
53         prop: 'enabled',
54         flexGrow: 1,
55         cellClass: 'text-center',
56         cellTransformation: CellTemplate.checkIcon
57       },
58       {
59         name: this.i18n('Always-On'),
60         prop: 'always_on',
61         isHidden: true,
62         flexGrow: 1,
63         cellClass: 'text-center',
64         cellTransformation: CellTemplate.checkIcon
65       }
66     ];
67     const getModuleUri = () =>
68       this.selection.first() && encodeURIComponent(this.selection.first().name);
69     this.tableActions = [
70       {
71         name: this.i18n('Edit'),
72         permission: 'update',
73         disable: () => {
74           if (!this.selection.hasSelection) {
75             return true;
76           }
77           // Disable the 'edit' button when the module has no options.
78           return Object.values(this.selection.first().options).length === 0;
79         },
80         routerLink: () => `/mgr-modules/edit/${getModuleUri()}`,
81         icon: Icons.edit
82       },
83       {
84         name: this.i18n('Enable'),
85         permission: 'update',
86         click: () => this.updateModuleState(),
87         disable: () => this.isTableActionDisabled('enabled'),
88         icon: Icons.start
89       },
90       {
91         name: this.i18n('Disable'),
92         permission: 'update',
93         click: () => this.updateModuleState(),
94         disable: () => this.isTableActionDisabled('disabled'),
95         disableDesc: () => this.getTableActionDisabledDesc(),
96         icon: Icons.stop
97       }
98     ];
99   }
100
101   getModuleList(context: CdTableFetchDataContext) {
102     this.mgrModuleService.list().subscribe(
103       (resp: object[]) => {
104         this.modules = resp;
105       },
106       () => {
107         context.error();
108       }
109     );
110   }
111
112   updateSelection(selection: CdTableSelection) {
113     this.selection = selection;
114   }
115
116   /**
117    * Check if the table action is disabled.
118    * @param state The expected module state, e.g. ``enabled`` or ``disabled``.
119    * @returns If the specified state is validated to true or no selection is
120    *   done, then ``true`` is returned, otherwise ``false``.
121    */
122   isTableActionDisabled(state: 'enabled' | 'disabled') {
123     if (!this.selection.hasSelection) {
124       return true;
125     }
126     const selected = this.selection.first();
127     // Make sure the user can't modify the run state of the 'Dashboard' module.
128     // This check is only done in the UI because the REST API should still be
129     // able to do so.
130     if (selected.name === 'dashboard') {
131       return true;
132     }
133     // Always-on modules can't be disabled.
134     if (selected.always_on) {
135       return true;
136     }
137     switch (state) {
138       case 'enabled':
139         return selected.enabled;
140       case 'disabled':
141         return !selected.enabled;
142     }
143   }
144
145   getTableActionDisabledDesc(): string | undefined {
146     if (this.selection.hasSelection) {
147       const selected = this.selection.first();
148       if (selected.always_on) {
149         return this.i18n('This Manager module is always on.');
150       }
151     }
152
153     return undefined;
154   }
155
156   /**
157    * Update the Ceph Mgr module state to enabled or disabled.
158    */
159   updateModuleState() {
160     if (!this.selection.hasSelection) {
161       return;
162     }
163
164     let $obs;
165     const fnWaitUntilReconnected = () => {
166       observableTimer(2000).subscribe(() => {
167         // Trigger an API request to check if the connection is
168         // re-established.
169         this.mgrModuleService.list().subscribe(
170           () => {
171             // Resume showing the notification toasties.
172             this.notificationService.suspendToasties(false);
173             // Unblock the whole UI.
174             this.blockUI.stop();
175             // Reload the data table content.
176             this.table.refreshBtn();
177           },
178           () => {
179             fnWaitUntilReconnected();
180           }
181         );
182       });
183     };
184
185     // Note, the Ceph Mgr is always restarted when a module
186     // is enabled/disabled.
187     const module = this.selection.first();
188     if (module.enabled) {
189       $obs = this.mgrModuleService.disable(module.name);
190     } else {
191       $obs = this.mgrModuleService.enable(module.name);
192     }
193     $obs.subscribe(
194       () => {},
195       () => {
196         // Suspend showing the notification toasties.
197         this.notificationService.suspendToasties(true);
198         // Block the whole UI to prevent user interactions until
199         // the connection to the backend is reestablished
200         this.blockUI.start(this.i18n('Reconnecting, please wait ...'));
201         fnWaitUntilReconnected();
202       }
203     );
204   }
205 }