summaryrefslogtreecommitdiffstats
path: root/glances/events.py
blob: 956364f3cfa797bd51948fd713cb5e411a16a4c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#

"""Manage Glances events (previously Glances logs in Glances < 3.1)."""

import time
from datetime import datetime

from glances.processes import glances_processes, sort_stats
from glances.logger import logger


class GlancesEvents(object):

    """This class manages events inside the Glances software.

    Events is a list of event (stored in the self.events_list var)
    event_state = "OK|CAREFUL|WARNING|CRITICAL"
    event_type = "CPU*|LOAD|MEM|MON"
    event_value = value

    Item (or event) is defined by:
      ["begin",
       "end",
       "WARNING|CRITICAL",
       "CPU|LOAD|MEM",
       MAX, AVG, MIN, SUM, COUNT,
       [top3 process list],
       "Processes description",
       "top sort key"]
    """

    def __init__(self, max_events=10):
        """Init the events class."""
        # Maximum size of the events list
        self.set_max_events(max_events)

        # Init the logs list
        self.events_list = []

    def set_max_events(self, max_events):
        """Set the maximum size of the events list."""
        self.max_events = max_events

    def get(self):
        """Return the raw events list."""
        return self.events_list

    def len(self):
        """Return the number of events in the logs list."""
        return self.events_list.__len__()

    def __event_exist(self, event_type):
        """Return the event position, if it exists.

        An event exist if:
        * end is < 0
        * event_type is matching
        Return -1 if the item is not found.
        """
        for i in range(self.len()):
            if self.events_list[i][1] < 0 and self.events_list[i][3] == event_type:
                return i
        return -1

    def get_event_sort_key(self, event_type):
        """Return the process sort key"""
        # Process sort depending on alert type
        if event_type.startswith("MEM"):
            # Sort TOP process by memory_percent
            ret = 'memory_percent'
        elif event_type.startswith("CPU_IOWAIT"):
            # Sort TOP process by io_counters (only for Linux OS)
            ret = 'io_counters'
        else:
            # Default sort is...
            ret = 'cpu_percent'
        return ret

    def set_process_sort(self, event_type):
        """Define the process auto sort key from the alert type."""
        if glances_processes.auto_sort:
            glances_processes.set_sort_key(self.get_event_sort_key(event_type))

    def reset_process_sort(self):
        """Reset the process auto sort key."""
        if glances_processes.auto_sort:
            glances_processes.set_sort_key('auto')

    def add(self, event_state, event_type, event_value, proc_list=None, proc_desc="", peak_time=6):
        """Add a new item to the logs list.

        If 'event' is a 'new one', add it at the beginning of the list.
        If 'event' is not a 'new one', update the list .
        If event < peak_time then the alert is not set.
        """
        proc_list = proc_list or glances_processes.getlist()

        # Add or update the log
        event_index = self.__event_exist(event_type)
        if event_index < 0:
            # Event did not exist, add it
            self._create_event(event_state, event_type, event_value, proc_desc)
        else:
            # Event exist, update it
            self._update_event(event_index,
                               event_state, event_type, event_value, proc_list, proc_desc, peak_time)

        return self.len()

    def _create_event(self, event_state, event_type, event_value, proc_desc):
        """Add a new item in the log list.

        Item is added only if the criticality (event_state) is WARNING or CRITICAL.
        """
        if event_state == "WARNING" or event_state == "CRITICAL":
            # Define the automatic process sort key
            self.set_process_sort(event_type)

            # Create the new log item
            # Time is stored in Epoch format
            # Epoch -> DMYHMS = datetime.fromtimestamp(epoch)
            item = [
                time.mktime(datetime.now().timetuple()),  # START DATE
                -1,  # END DATE
                event_state,  # STATE: WARNING|CRITICAL
                event_type,   # TYPE: CPU, LOAD, MEM...
                event_value,  # MAX
                event_value,  # AVG
                event_value,  # MIN
                event_value,  # SUM
                1,  # COUNT
                [],  # TOP 3 PROCESS LIST
                proc_desc,  # MONITORED PROCESSES DESC
                glances_processes.sort_key,
            ]  # TOP PROCESS SORT KEY

            # Add the item to the list
            self.events_list.insert(0, item)

            # Limit the list to 'max_events' items
            if self.len() > self.max_events:
                self.events_list.pop()

            return True
        else:
            return False

    def _update_event(self, event_index, event_state, event_type, event_value, proc_list, proc_desc, peak_time):
        """Update an event in the list"""
        if event_state == "OK" or event_state == "CAREFUL":
            # Reset the automatic process sort key
            self.reset_process_sort()

            # Set the end of the events
            end_time = time.mktime(datetime.now().timetuple())
            if end_time - self.events_list[event_index][0] > peak_time:
                # If event is > peak_time seconds
                self.events_list[event_index][1] = end_time
            else:
                # If event <= peak_time seconds, ignore
                self.events_list.remove(self.events_list[event_index])
        else:
            # Update the item
            self.set_process_sort(event_type)

            # State
            if event_state == "CRITICAL":
                self.events_list[event_index][2] = event_state
            # Min value
            self.events_list[event_index][6] = min(self.events_list[event_index][6], event_value)
            # Max value
            self.events_list[event_index][4] = max(self.events_list[event_index][4], event_value)
            # Average value
            self.events_list[event_index][7] += event_value
            self.events_list[event_index][8] += 1
            self.events_list[event_index][5] = self.events_list[event_index][7] / self.events_list[event_index][8]

            # TOP PROCESS LIST (only for CRITICAL ALERT)
            if event_state == "CRITICAL":
                events_sort_key = self.get_event_sort_key(event_type)
                # Sort the current process list to retrieve the TOP 3 processes
                self.events_list[event_index][9] = sort_stats(proc_list, events_sort_key)[0:3]
                self.events_list[event_index][11] = events_sort_key

            # MONITORED PROCESSES DESC
            self.events_list[event_index][10] = proc_desc

        return True

    def clean(self, critical=False):
        """Clean the logs list by deleting finished items.

        By default, only delete WARNING message.
        If critical = True, also delete CRITICAL message.
        """
        # Create a new clean list
        clean_events_list = []
        while self.len() > 0:
            item = self.events_list.pop()
            if item[1] < 0 or (not critical and item[2].startswith("CRITICAL")):
                clean_events_list.insert(0, item)
        # The list is now the clean one
        self.events_list = clean_events_list
        return self.len()


glances_events = GlancesEvents()