]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
a662a898b163bca68dc41aca67b607e64e67e249
[ceph-ci.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.sidebarSubject.subscribe((forceClose) => {
106         if (forceClose) {
107           this.isSidebarOpened = false;
108         } else {
109           this.isSidebarOpened = !this.isSidebarOpened;
110         }
111
112         window.clearTimeout(this.timeout);
113         this.timeout = window.setTimeout(() => {
114           this.cdRef.detectChanges();
115         }, 0);
116       })
117     );
118
119     this.subs.add(
120       this.summaryService.subscribe((summary) => {
121         this._handleTasks(summary.executing_tasks);
122
123         this.mutex.acquire().then((release) => {
124           _.filter(
125             summary.finished_tasks,
126             (task: FinishedTask) => !this.last_task || moment(task.end_time).isAfter(this.last_task)
127           ).forEach((task) => {
128             const config = this.notificationService.finishedTaskToNotification(task, task.success);
129             const notification = new CdNotification(config);
130             notification.timestamp = task.end_time;
131             notification.duration = task.duration;
132
133             if (!this.last_task || moment(task.end_time).isAfter(this.last_task)) {
134               this.last_task = task.end_time;
135               window.localStorage.setItem('last_task', this.last_task);
136             }
137
138             this.notificationService.save(notification);
139           });
140
141           this.cdRef.detectChanges();
142
143           release();
144         });
145       })
146     );
147   }
148
149   _handleTasks(executingTasks: ExecutingTask[]) {
150     for (const executingTask of executingTasks) {
151       executingTask.description = this.taskMessageService.getRunningTitle(executingTask);
152     }
153     this.executingTasks = executingTasks;
154   }
155
156   private triggerPrometheusAlerts() {
157     this.prometheusAlertService.refresh(true);
158     this.prometheusNotificationService.refresh();
159   }
160
161   removeAll() {
162     this.notificationService.removeAll();
163   }
164
165   remove(index: number) {
166     this.notificationService.remove(index);
167   }
168
169   closeSidebar() {
170     this.isSidebarOpened = false;
171   }
172
173   trackByFn(index: number) {
174     return index;
175   }
176
177   silence(data: CdNotification) {
178     const datetimeFormat = 'YYYY-MM-DD HH:mm';
179     const resource = $localize`silence`;
180     const matcher: AlertmanagerSilenceMatcher = {
181       name: 'alertname',
182       value: data['title'].split(' ')[0],
183       isRegex: false
184     };
185     const silencePayload: AlertmanagerSilence = {
186       matchers: [matcher],
187       startsAt: moment(moment().format(datetimeFormat)).toISOString(),
188       endsAt: moment(moment().add(2, 'hours').format(datetimeFormat)).toISOString(),
189       createdBy: this.authStorageService.getUsername(),
190       comment: 'Silence created from the alert notification'
191     };
192     let msg = '';
193
194     data.alertSilenced = true;
195     msg = msg.concat(` ${matcher.name} - ${matcher.value},`);
196     const title = `${this.succeededLabels.CREATED} ${resource} for ${msg.slice(0, -1)}`;
197     this.prometheusService.setSilence(silencePayload).subscribe((resp) => {
198       if (data) {
199         data.silenceId = resp.body['silenceId'];
200       }
201       this.notificationService.show(
202         NotificationType.success,
203         title,
204         undefined,
205         undefined,
206         'Prometheus'
207       );
208     });
209   }
210
211   expire(data: CdNotification) {
212     data.alertSilenced = false;
213     this.prometheusService.expireSilence(data.silenceId).subscribe(
214       () => {
215         this.notificationService.show(
216           NotificationType.success,
217           `${this.succeededLabels.EXPIRED} ${data.silenceId}`,
218           undefined,
219           undefined,
220           'Prometheus'
221         );
222       },
223       (resp) => {
224         resp['application'] = 'Prometheus';
225       }
226     );
227   }
228 }