]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/blob
ae836d406331b986fa97cf7792acde29f041cf70
[ceph.git] /
1 import {
2   ChangeDetectionStrategy,
3   ChangeDetectorRef,
4   Component,
5   HostBinding,
6   NgZone,
7   OnDestroy,
8   OnInit
9 } from '@angular/core';
10
11 import { Mutex } from 'async-mutex';
12 import _ from 'lodash';
13 import moment from 'moment';
14 import { Subscription } from 'rxjs';
15
16 import { PrometheusService } from '~/app/shared/api/prometheus.service';
17 import { SucceededActionLabelsI18n } from '~/app/shared/constants/app.constants';
18 import { Icons } from '~/app/shared/enum/icons.enum';
19 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
20 import {
21   AlertmanagerSilence,
22   AlertmanagerSilenceMatcher
23 } from '~/app/shared/models/alertmanager-silence';
24 import { CdNotification } from '~/app/shared/models/cd-notification';
25 import { ExecutingTask } from '~/app/shared/models/executing-task';
26 import { FinishedTask } from '~/app/shared/models/finished-task';
27 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
28 import { NotificationService } from '~/app/shared/services/notification.service';
29 import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
30 import { PrometheusNotificationService } from '~/app/shared/services/prometheus-notification.service';
31 import { SummaryService } from '~/app/shared/services/summary.service';
32 import { TaskMessageService } from '~/app/shared/services/task-message.service';
33
34 @Component({
35   selector: 'cd-notifications-sidebar',
36   templateUrl: './notifications-sidebar.component.html',
37   styleUrls: ['./notifications-sidebar.component.scss'],
38   changeDetection: ChangeDetectionStrategy.OnPush
39 })
40 export class NotificationsSidebarComponent implements OnInit, OnDestroy {
41   @HostBinding('class.active') isSidebarOpened = false;
42
43   notifications: CdNotification[];
44   private interval: number;
45   private timeout: number;
46
47   executingTasks: ExecutingTask[] = [];
48
49   private subs = new Subscription();
50
51   icons = Icons;
52
53   // Tasks
54   last_task = '';
55   mutex = new Mutex();
56
57   simplebar = {
58     autoHide: false
59   };
60
61   constructor(
62     public notificationService: NotificationService,
63     private summaryService: SummaryService,
64     private taskMessageService: TaskMessageService,
65     private prometheusNotificationService: PrometheusNotificationService,
66     private succeededLabels: SucceededActionLabelsI18n,
67     private authStorageService: AuthStorageService,
68     private prometheusAlertService: PrometheusAlertService,
69     private prometheusService: PrometheusService,
70     private ngZone: NgZone,
71     private cdRef: ChangeDetectorRef
72   ) {
73     this.notifications = [];
74   }
75
76   ngOnDestroy() {
77     window.clearInterval(this.interval);
78     window.clearTimeout(this.timeout);
79     this.subs.unsubscribe();
80   }
81
82   ngOnInit() {
83     this.last_task = window.localStorage.getItem('last_task');
84
85     const permissions = this.authStorageService.getPermissions();
86     if (permissions.prometheus.read && permissions.configOpt.read) {
87       this.triggerPrometheusAlerts();
88       this.ngZone.runOutsideAngular(() => {
89         this.interval = window.setInterval(() => {
90           this.ngZone.run(() => {
91             this.triggerPrometheusAlerts();
92           });
93         }, 5000);
94       });
95     }
96
97     this.subs.add(
98       this.notificationService.data$.subscribe((notifications: CdNotification[]) => {
99         this.notifications = _.orderBy(notifications, ['timestamp'], ['desc']);
100         this.cdRef.detectChanges();
101       })
102     );
103
104     this.subs.add(
105       this.notificationService.panelState$.subscribe((state) => {
106         this.isSidebarOpened = state.isOpen && !state.useNewPanel;
107         this.cdRef.detectChanges();
108       })
109     );
110
111     this.subs.add(
112       this.summaryService.subscribe((summary) => {
113         this._handleTasks(summary.executing_tasks);
114
115         this.mutex.acquire().then((release) => {
116           _.filter(
117             summary.finished_tasks,
118             (task: FinishedTask) => !this.last_task || moment(task.end_time).isAfter(this.last_task)
119           ).forEach((task) => {
120             const config = this.notificationService.finishedTaskToNotification(task, task.success);
121             const notification = new CdNotification(config);
122             notification.timestamp = task.end_time;
123             notification.duration = task.duration;
124
125             if (!this.last_task || moment(task.end_time).isAfter(this.last_task)) {
126               this.last_task = task.end_time;
127               window.localStorage.setItem('last_task', this.last_task);
128             }
129
130             this.notificationService.save(notification);
131           });
132
133           this.cdRef.detectChanges();
134
135           release();
136         });
137       })
138     );
139   }
140
141   _handleTasks(executingTasks: ExecutingTask[]) {
142     for (const executingTask of executingTasks) {
143       executingTask.description = this.taskMessageService.getRunningTitle(executingTask);
144     }
145     this.executingTasks = executingTasks;
146   }
147
148   private triggerPrometheusAlerts() {
149     this.prometheusAlertService.refresh();
150     this.prometheusNotificationService.refresh();
151   }
152
153   removeAll() {
154     this.notificationService.removeAll();
155   }
156
157   remove(index: number) {
158     this.notificationService.remove(index);
159   }
160
161   closeSidebar() {
162     this.notificationService.toggleSidebar(false, false);
163   }
164
165   trackByFn(index: number) {
166     return index;
167   }
168
169   silence(data: CdNotification) {
170     const datetimeFormat = 'YYYY-MM-DD HH:mm';
171     const resource = $localize`silence`;
172     const matcher: AlertmanagerSilenceMatcher = {
173       name: 'alertname',
174       value: data['title'].split(' ')[0],
175       isRegex: false
176     };
177     const silencePayload: AlertmanagerSilence = {
178       matchers: [matcher],
179       startsAt: moment(moment().format(datetimeFormat)).toISOString(),
180       endsAt: moment(moment().add(2, 'hours').format(datetimeFormat)).toISOString(),
181       createdBy: this.authStorageService.getUsername(),
182       comment: 'Silence created from the alert notification'
183     };
184     let msg = '';
185
186     data.alertSilenced = true;
187     msg = msg.concat(` ${matcher.name} - ${matcher.value},`);
188     const title = `${this.succeededLabels.CREATED} ${resource} for ${msg.slice(0, -1)}`;
189     this.prometheusService.setSilence(silencePayload).subscribe((resp) => {
190       if (data) {
191         data.silenceId = resp.body['silenceId'];
192       }
193       this.notificationService.show(
194         NotificationType.success,
195         title,
196         undefined,
197         undefined,
198         'Prometheus'
199       );
200     });
201   }
202
203   expire(data: CdNotification) {
204     data.alertSilenced = false;
205     this.prometheusService.expireSilence(data.silenceId).subscribe(
206       () => {
207         this.notificationService.show(
208           NotificationType.success,
209           `${this.succeededLabels.EXPIRED} ${data.silenceId}`,
210           undefined,
211           undefined,
212           'Prometheus'
213         );
214       },
215       (resp) => {
216         resp['application'] = 'Prometheus';
217       }
218     );
219   }
220 }