]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-daemon-list/service-daemon-list.component.ts
import quincy beta 17.1.0
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / services / service-daemon-list / service-daemon-list.component.ts
CommitLineData
9f95a23c
TL
1import {
2 AfterViewInit,
3 Component,
4 Input,
5 OnChanges,
6 OnDestroy,
7 OnInit,
8 QueryList,
9 TemplateRef,
e306af50 10 ViewChild,
9f95a23c
TL
11 ViewChildren
12} from '@angular/core';
9f95a23c 13
f67539c2 14import _ from 'lodash';
9f95a23c 15import { Observable, Subscription } from 'rxjs';
20effc67 16import { take } from 'rxjs/operators';
f67539c2
TL
17
18import { CephServiceService } from '~/app/shared/api/ceph-service.service';
20effc67 19import { DaemonService } from '~/app/shared/api/daemon.service';
f67539c2
TL
20import { HostService } from '~/app/shared/api/host.service';
21import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
20effc67 22import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
f67539c2
TL
23import { TableComponent } from '~/app/shared/datatable/table/table.component';
24import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
b3b6e05e 25import { Icons } from '~/app/shared/enum/icons.enum';
20effc67
TL
26import { NotificationType } from '~/app/shared/enum/notification-type.enum';
27import { CdTableAction } from '~/app/shared/models/cd-table-action';
f67539c2
TL
28import { CdTableColumn } from '~/app/shared/models/cd-table-column';
29import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
20effc67 30import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
f67539c2 31import { Daemon } from '~/app/shared/models/daemon.interface';
20effc67 32import { Permissions } from '~/app/shared/models/permissions';
b3b6e05e 33import { CephServiceSpec } from '~/app/shared/models/service.interface';
f67539c2 34import { RelativeDatePipe } from '~/app/shared/pipes/relative-date.pipe';
20effc67
TL
35import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
36import { NotificationService } from '~/app/shared/services/notification.service';
9f95a23c
TL
37
38@Component({
39 selector: 'cd-service-daemon-list',
40 templateUrl: './service-daemon-list.component.html',
41 styleUrls: ['./service-daemon-list.component.scss']
42})
43export class ServiceDaemonListComponent implements OnInit, OnChanges, AfterViewInit, OnDestroy {
e306af50
TL
44 @ViewChild('statusTpl', { static: true })
45 statusTpl: TemplateRef<any>;
46
b3b6e05e
TL
47 @ViewChild('listTpl', { static: true })
48 listTpl: TemplateRef<any>;
49
9f95a23c
TL
50 @ViewChildren('daemonsTable')
51 daemonsTableTpls: QueryList<TemplateRef<TableComponent>>;
52
53 @Input()
54 serviceName?: string;
55
56 @Input()
57 hostname?: string;
58
b3b6e05e
TL
59 @Input()
60 flag?: string;
61
62 icons = Icons;
63
9f95a23c 64 daemons: Daemon[] = [];
b3b6e05e 65 services: Array<CephServiceSpec> = [];
9f95a23c 66 columns: CdTableColumn[] = [];
b3b6e05e 67 serviceColumns: CdTableColumn[] = [];
20effc67
TL
68 tableActions: CdTableAction[];
69 selection = new CdTableSelection();
70 permissions: Permissions;
9f95a23c
TL
71
72 hasOrchestrator = false;
adb31ebb 73 showDocPanel = false;
9f95a23c
TL
74
75 private daemonsTable: TableComponent;
76 private daemonsTableTplsSub: Subscription;
b3b6e05e 77 private serviceSub: Subscription;
9f95a23c
TL
78
79 constructor(
9f95a23c
TL
80 private hostService: HostService,
81 private cephServiceService: CephServiceService,
f67539c2 82 private orchService: OrchestratorService,
20effc67
TL
83 private relativeDatePipe: RelativeDatePipe,
84 public actionLabels: ActionLabelsI18n,
85 private authStorageService: AuthStorageService,
86 private daemonService: DaemonService,
87 private notificationService: NotificationService
9f95a23c
TL
88 ) {}
89
90 ngOnInit() {
20effc67
TL
91 this.permissions = this.authStorageService.getPermissions();
92 this.tableActions = [
93 {
94 permission: 'update',
95 icon: Icons.start,
96 click: () => this.daemonAction('start'),
97 name: this.actionLabels.START,
98 disable: () => this.actionDisabled('start')
99 },
100 {
101 permission: 'update',
102 icon: Icons.stop,
103 click: () => this.daemonAction('stop'),
104 name: this.actionLabels.STOP,
105 disable: () => this.actionDisabled('stop')
106 },
107 {
108 permission: 'update',
109 icon: Icons.restart,
110 click: () => this.daemonAction('restart'),
111 name: this.actionLabels.RESTART,
112 disable: () => this.actionDisabled('restart')
113 },
114 {
115 permission: 'update',
116 icon: Icons.deploy,
117 click: () => this.daemonAction('redeploy'),
118 name: this.actionLabels.REDEPLOY,
119 disable: () => this.actionDisabled('redeploy')
120 }
121 ];
9f95a23c
TL
122 this.columns = [
123 {
f67539c2 124 name: $localize`Hostname`,
9f95a23c 125 prop: 'hostname',
b3b6e05e 126 flexGrow: 2,
9f95a23c
TL
127 filterable: true
128 },
129 {
f67539c2 130 name: $localize`Daemon type`,
9f95a23c
TL
131 prop: 'daemon_type',
132 flexGrow: 1,
133 filterable: true
134 },
135 {
f67539c2 136 name: $localize`Daemon ID`,
9f95a23c
TL
137 prop: 'daemon_id',
138 flexGrow: 1,
139 filterable: true
140 },
141 {
f67539c2 142 name: $localize`Container ID`,
9f95a23c 143 prop: 'container_id',
b3b6e05e 144 flexGrow: 2,
1911f103
TL
145 filterable: true,
146 cellTransformation: CellTemplate.truncate,
147 customTemplateConfig: {
148 length: 12
149 }
9f95a23c
TL
150 },
151 {
f67539c2 152 name: $localize`Container Image name`,
9f95a23c
TL
153 prop: 'container_image_name',
154 flexGrow: 3,
155 filterable: true
156 },
157 {
f67539c2 158 name: $localize`Container Image ID`,
9f95a23c 159 prop: 'container_image_id',
b3b6e05e 160 flexGrow: 2,
1911f103
TL
161 filterable: true,
162 cellTransformation: CellTemplate.truncate,
163 customTemplateConfig: {
164 length: 12
165 }
9f95a23c
TL
166 },
167 {
f67539c2 168 name: $localize`Version`,
9f95a23c
TL
169 prop: 'version',
170 flexGrow: 1,
171 filterable: true
172 },
173 {
f67539c2 174 name: $localize`Status`,
9f95a23c
TL
175 prop: 'status_desc',
176 flexGrow: 1,
e306af50
TL
177 filterable: true,
178 cellTemplate: this.statusTpl
9f95a23c
TL
179 },
180 {
f67539c2 181 name: $localize`Last Refreshed`,
9f95a23c 182 prop: 'last_refresh',
f67539c2 183 pipe: this.relativeDatePipe,
b3b6e05e
TL
184 flexGrow: 1
185 },
186 {
187 name: $localize`Daemon Events`,
188 prop: 'events',
189 flexGrow: 5,
190 cellTemplate: this.listTpl
191 }
192 ];
193
194 this.serviceColumns = [
195 {
196 name: $localize`Service Name`,
197 prop: 'service_name',
198 flexGrow: 2,
199 filterable: true
200 },
201 {
202 name: $localize`Service Type`,
203 prop: 'service_type',
204 flexGrow: 1,
205 filterable: true
206 },
207 {
208 name: $localize`Service Events`,
209 prop: 'events',
210 flexGrow: 5,
211 cellTemplate: this.listTpl
9f95a23c
TL
212 }
213 ];
214
215 this.orchService.status().subscribe((data: { available: boolean }) => {
216 this.hasOrchestrator = data.available;
adb31ebb 217 this.showDocPanel = !data.available;
9f95a23c
TL
218 });
219 }
220
221 ngOnChanges() {
222 if (!_.isUndefined(this.daemonsTable)) {
223 this.daemonsTable.reloadData();
224 }
225 }
226
227 ngAfterViewInit() {
228 this.daemonsTableTplsSub = this.daemonsTableTpls.changes.subscribe(
229 (tableRefs: QueryList<TableComponent>) => {
230 this.daemonsTable = tableRefs.first;
231 }
232 );
233 }
234
235 ngOnDestroy() {
236 if (this.daemonsTableTplsSub) {
237 this.daemonsTableTplsSub.unsubscribe();
238 }
b3b6e05e
TL
239 if (this.serviceSub) {
240 this.serviceSub.unsubscribe();
241 }
9f95a23c
TL
242 }
243
f67539c2 244 getStatusClass(row: Daemon): string {
e306af50
TL
245 return _.get(
246 {
247 '-1': 'badge-danger',
248 '0': 'badge-warning',
249 '1': 'badge-success'
250 },
f67539c2 251 row.status,
e306af50
TL
252 'badge-dark'
253 );
254 }
255
9f95a23c
TL
256 getDaemons(context: CdTableFetchDataContext) {
257 let observable: Observable<Daemon[]>;
258 if (this.hostname) {
259 observable = this.hostService.getDaemons(this.hostname);
260 } else if (this.serviceName) {
261 observable = this.cephServiceService.getDaemons(this.serviceName);
262 } else {
263 this.daemons = [];
264 return;
265 }
266 observable.subscribe(
267 (daemons: Daemon[]) => {
268 this.daemons = daemons;
20effc67 269 this.sortDaemonEvents();
9f95a23c
TL
270 },
271 () => {
272 this.daemons = [];
273 context.error();
274 }
275 );
276 }
b3b6e05e 277
20effc67
TL
278 sortDaemonEvents() {
279 this.daemons.forEach((daemon: any) => {
280 daemon.events?.sort((event1: any, event2: any) => {
281 return new Date(event2.created).getTime() - new Date(event1.created).getTime();
282 });
283 });
284 }
b3b6e05e
TL
285 getServices(context: CdTableFetchDataContext) {
286 this.serviceSub = this.cephServiceService.list(this.serviceName).subscribe(
287 (services: CephServiceSpec[]) => {
288 this.services = services;
289 },
290 () => {
291 this.services = [];
292 context.error();
293 }
294 );
295 }
296
297 trackByFn(_index: any, item: any) {
298 return item.created;
299 }
20effc67
TL
300
301 updateSelection(selection: CdTableSelection) {
302 this.selection = selection;
303 }
304
305 daemonAction(actionType: string) {
306 this.daemonService
307 .action(this.selection.first()?.daemon_name, actionType)
308 .pipe(take(1))
309 .subscribe({
310 next: (resp) => {
311 this.notificationService.show(
312 NotificationType.success,
313 `Daemon ${actionType} scheduled`,
314 resp.body.toString()
315 );
316 },
317 error: (resp) => {
318 this.notificationService.show(
319 NotificationType.error,
320 'Daemon action failed',
321 resp.body.toString()
322 );
323 }
324 });
325 }
326
327 actionDisabled(actionType: string) {
328 if (this.selection?.hasSelection) {
329 const daemon = this.selection.selected[0];
330 if (daemon.daemon_type === 'mon' || daemon.daemon_type === 'mgr') {
331 return true; // don't allow actions on mon and mgr, dashboard requires them.
332 }
333 switch (actionType) {
334 case 'start':
335 if (daemon.status_desc === 'running') {
336 return true;
337 }
338 break;
339 case 'stop':
340 if (daemon.status_desc === 'stopped') {
341 return true;
342 }
343 break;
344 }
345 return false;
346 }
347 return true; // if no selection then disable everything
348 }
9f95a23c 349}