~landscape/landscape-client/staging

« back to all changes in this revision

Viewing changes to landscape/monitor/cpuusage.py

  • Committer: Simon Poirier
  • Date: 2017-08-17 23:23:07 UTC
  • mfrom: (0.1.996)
  • Revision ID: simon.poirier@canonical.com-20170817232307-6je8sd48dh7731s2
Merged from master r996

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import time
 
2
import logging
 
3
 
 
4
from landscape.accumulate import Accumulator
 
5
from landscape.lib.monitor import CoverageMonitor
 
6
from landscape.monitor.plugin import MonitorPlugin
 
7
 
 
8
LAST_MESURE_KEY = "last-cpu-usage-measure"
 
9
ACCUMULATOR_KEY = "cpu-usage-accumulator"
 
10
 
 
11
 
 
12
class CPUUsage(MonitorPlugin):
 
13
    """
 
14
    Plugin that captures CPU usage information.
 
15
    """
 
16
    persist_name = "cpu-usage"
 
17
    scope = "cpu"
 
18
    # Prevent the Plugin base-class from scheduling looping calls.
 
19
    run_interval = None
 
20
 
 
21
    def __init__(self, interval=30, monitor_interval=60 * 60,
 
22
                 create_time=time.time):
 
23
        self._interval = interval
 
24
        self._monitor_interval = monitor_interval
 
25
        self._cpu_usage_points = []
 
26
        self._create_time = create_time
 
27
        self._stat_file = "/proc/stat"
 
28
 
 
29
    def register(self, registry):
 
30
        super(CPUUsage, self).register(registry)
 
31
        self._accumulate = Accumulator(self._persist, registry.step_size)
 
32
 
 
33
        self.registry.reactor.call_every(self._interval, self.run)
 
34
 
 
35
        self._monitor = CoverageMonitor(self._interval, 0.8,
 
36
                                        "CPU usage snapshot",
 
37
                                        create_time=self._create_time)
 
38
        self.registry.reactor.call_every(self._monitor_interval,
 
39
                                         self._monitor.log)
 
40
        self.registry.reactor.call_on("stop", self._monitor.log, priority=2000)
 
41
        self.call_on_accepted("cpu-usage", self.send_message, True)
 
42
 
 
43
    def create_message(self):
 
44
        cpu_points = self._cpu_usage_points
 
45
        self._cpu_usage_points = []
 
46
        return {"type": "cpu-usage", "cpu-usages": cpu_points}
 
47
 
 
48
    def send_message(self, urgent=False):
 
49
        message = self.create_message()
 
50
        if len(message["cpu-usages"]):
 
51
            self.registry.broker.send_message(message, self._session_id,
 
52
                                              urgent=urgent)
 
53
 
 
54
    def exchange(self, urgent=False):
 
55
        self.registry.broker.call_if_accepted("cpu-usage",
 
56
                                              self.send_message, urgent)
 
57
 
 
58
    def run(self):
 
59
        self._monitor.ping()
 
60
        new_timestamp = int(self._create_time())
 
61
        new_cpu_usage = self._get_cpu_usage(self._stat_file)
 
62
 
 
63
        step_data = None
 
64
        if new_cpu_usage is not None:
 
65
            step_data = self._accumulate(new_timestamp, new_cpu_usage,
 
66
                                        ACCUMULATOR_KEY)
 
67
        if step_data is not None:
 
68
            self._cpu_usage_points.append(step_data)
 
69
 
 
70
    def _get_cpu_usage(self, stat_file):
 
71
        """
 
72
        This method computes the CPU usage from C{stat_file}.
 
73
        """
 
74
        result = None
 
75
        try:
 
76
            with open(stat_file, "r") as f:
 
77
                # The first line of the file is the CPU information aggregated
 
78
                # across cores.
 
79
                stat = f.readline()
 
80
        except IOError:
 
81
            logging.error("Could not open %s for reading, "
 
82
                          "CPU usage cannot be computed.", stat_file)
 
83
            return None
 
84
 
 
85
        # The cpu line is composed of:
 
86
        # ["cpu", user, nice, system, idle, iowait, irq, softirq, steal, guest,
 
87
        # guest nice]
 
88
        # The fields are a sum of USER_HZ quantums since boot spent in each
 
89
        # "category". We need to keep track of what the previous measure was,
 
90
        # since the current CPU usage will be calculated on the delta between
 
91
        # the previous measure and the current measure.
 
92
        # Remove the trailing "\n"
 
93
        fields = stat.split()[1:]
 
94
        idle = int(fields[3])
 
95
        value = sum(int(i) for i in fields)
 
96
 
 
97
        previous = self._persist.get(LAST_MESURE_KEY)
 
98
        if previous is not None and value != previous[0]:
 
99
            delta = value - previous[0]
 
100
            if delta >= 0:
 
101
                result = (delta - idle + previous[1]) / float(delta)
 
102
 
 
103
        self._persist.set(LAST_MESURE_KEY, (value, idle))
 
104
 
 
105
        return result