]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.ts
import 15.2.4
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / mgr-modules / mgr-module-list / mgr-module-list.component.ts
CommitLineData
11fdf7f2
TL
1import { Component, ViewChild } from '@angular/core';
2
3import { I18n } from '@ngx-translate/i18n-polyfill';
4import { BlockUI, NgBlockUI } from 'ng-block-ui';
5import { timer as observableTimer } from 'rxjs';
6
7import { MgrModuleService } from '../../../../shared/api/mgr-module.service';
e306af50 8import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
11fdf7f2
TL
9import { TableComponent } from '../../../../shared/datatable/table/table.component';
10import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
9f95a23c 11import { Icons } from '../../../../shared/enum/icons.enum';
11fdf7f2
TL
12import { CdTableAction } from '../../../../shared/models/cd-table-action';
13import { CdTableColumn } from '../../../../shared/models/cd-table-column';
14import { CdTableFetchDataContext } from '../../../../shared/models/cd-table-fetch-data-context';
15import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
16import { Permission } from '../../../../shared/models/permissions';
17import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
18import { 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})
e306af50 25export class MgrModuleListComponent extends ListWithDetails {
9f95a23c 26 @ViewChild(TableComponent, { static: true })
11fdf7f2
TL
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 ) {
e306af50 43 super();
11fdf7f2
TL
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
9f95a23c
TL
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
11fdf7f2
TL
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()}`,
9f95a23c 81 icon: Icons.edit
11fdf7f2
TL
82 },
83 {
84 name: this.i18n('Enable'),
85 permission: 'update',
86 click: () => this.updateModuleState(),
87 disable: () => this.isTableActionDisabled('enabled'),
9f95a23c 88 icon: Icons.start
11fdf7f2
TL
89 },
90 {
91 name: this.i18n('Disable'),
92 permission: 'update',
93 click: () => this.updateModuleState(),
94 disable: () => this.isTableActionDisabled('disabled'),
92f5a8d4 95 disableDesc: () => this.getTableActionDisabledDesc(),
9f95a23c 96 icon: Icons.stop
11fdf7f2
TL
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 }
92f5a8d4 126 const selected = this.selection.first();
11fdf7f2
TL
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.
92f5a8d4
TL
130 if (selected.name === 'dashboard') {
131 return true;
132 }
133 // Always-on modules can't be disabled.
134 if (selected.always_on) {
11fdf7f2
TL
135 return true;
136 }
137 switch (state) {
138 case 'enabled':
92f5a8d4 139 return selected.enabled;
11fdf7f2 140 case 'disabled':
92f5a8d4
TL
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 }
11fdf7f2 151 }
9f95a23c
TL
152
153 return undefined;
11fdf7f2
TL
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}