~tribaal/+junk/landscape-client-14.12-0ubuntu0.10.04

« back to all changes in this revision

Viewing changes to landscape/monitor/processorinfo.py

  • Committer: Chris Glass
  • Date: 2014-12-15 06:54:28 UTC
  • Revision ID: chris.glass@canonical.com-20141215065428-e23g6yyvrsvyb656
Imported pristine tarball.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import logging
 
2
import os
 
3
import re
 
4
 
 
5
from landscape.plugin import PluginConfigError
 
6
from landscape.monitor.plugin import MonitorPlugin
 
7
 
 
8
 
 
9
class ProcessorInfo(MonitorPlugin):
 
10
    """Plugin captures information about the processor(s) in this machine.
 
11
 
 
12
    This plugin runs once per client session.  When processor
 
13
    information is retrieved it's compared against the last known
 
14
    processor information, which is saved in persistent storage.  A
 
15
    message is only put on the message queue if the latest processor
 
16
    information differs from the last known processor information.
 
17
 
 
18
    The information available from /proc/cpuinfo varies per platform.
 
19
    For example, an Apple PowerMac Dual G5 doesn't contain a vendor ID
 
20
    and provides the processor name in the 'cpu' field, as opposed to
 
21
    the 'model name' field used on x86-based hardware.  For reasons
 
22
    such as this, the schema of the data reported by this plugin is
 
23
    flexible.  Only 'processor-id' and 'model' are guaranteed to be
 
24
    present.
 
25
 
 
26
    In order to deal with the vagaries of parsing /proc/cpu
 
27
    information on the various platforms we support, message
 
28
    generation is deferred to per-platform message factories.
 
29
 
 
30
    @param delay: Set the starting delay.
 
31
    @param machine_name: The machine name to report.
 
32
    @param source_filename: The filesystem path to read information from.
 
33
    """
 
34
 
 
35
    persist_name = "processor-info"
 
36
    scope = "cpu"
 
37
    # Prevent the Plugin base-class from scheduling looping calls.
 
38
    run_interval = None
 
39
 
 
40
    def __init__(self, delay=2, machine_name=None,
 
41
                 source_filename="/proc/cpuinfo"):
 
42
        self._delay = delay
 
43
        self._source_filename = source_filename
 
44
 
 
45
        if machine_name is None:
 
46
            machine_name = os.uname()[4]
 
47
 
 
48
        self._cpu_info_reader = self._create_cpu_info_reader(machine_name,
 
49
                                                             source_filename)
 
50
 
 
51
    def _create_cpu_info_reader(self, machine_name, source_filename):
 
52
        """Return a message factory suitable for the specified machine name."""
 
53
        for pair in message_factories:
 
54
            regexp = re.compile(pair[0])
 
55
 
 
56
            if regexp.match(machine_name):
 
57
                return pair[1](source_filename)
 
58
 
 
59
        raise PluginConfigError("A processor info reader for '%s' is not "
 
60
                                "available." % machine_name)
 
61
 
 
62
    def register(self, registry):
 
63
        """Register this plugin with the specified plugin registry."""
 
64
        super(ProcessorInfo, self).register(registry)
 
65
        self.registry.reactor.call_later(self._delay, self.run)
 
66
        self.call_on_accepted("processor-info", self.send_message, True)
 
67
 
 
68
    def create_message(self):
 
69
        """Retrieve processor information and generate a message."""
 
70
        return {"type": "processor-info",
 
71
                "processors": self._cpu_info_reader.create_message()}
 
72
 
 
73
    def send_message(self, urgent=False):
 
74
        dirty = False
 
75
        message = self.create_message()
 
76
 
 
77
        for processor in message["processors"]:
 
78
            key = ("processor", str(processor["processor-id"]))
 
79
            cached_processor = self._persist.get(key)
 
80
            if cached_processor is None:
 
81
                cached_processor = {}
 
82
                self._update(cached_processor, processor)
 
83
                dirty = True
 
84
            else:
 
85
                if self._has_changed(cached_processor, processor):
 
86
                    self._update(cached_processor, processor)
 
87
                    dirty = True
 
88
 
 
89
        if dirty:
 
90
            logging.info("Queueing message with updated processor info.")
 
91
            self.registry.broker.send_message(
 
92
                message, self._session_id, urgent=urgent)
 
93
 
 
94
    def run(self, urgent=False):
 
95
        """Create a message and put it on the message queue."""
 
96
        self.registry.broker.call_if_accepted("processor-info",
 
97
                                              self.send_message, urgent)
 
98
 
 
99
    def _has_changed(self, processor, message):
 
100
        """Returns true if processor details changed since the last read."""
 
101
        if processor["model"] != message["model"]:
 
102
            return True
 
103
 
 
104
        if processor["vendor"] != message.get("vendor", ""):
 
105
            return True
 
106
 
 
107
        if processor["cache_size"] != message.get("cache-size", -1):
 
108
            return True
 
109
 
 
110
        return False
 
111
 
 
112
    def _update(self, processor, message):
 
113
        """Update the processor details with current values."""
 
114
        processor["id"] = message["processor-id"]
 
115
        processor["model"] = message["model"]
 
116
        processor["cache_size"] = message.get("cache-size", -1)
 
117
        processor["vendor"] = message.get("vendor", "")
 
118
        self._persist.set(("processor", str(message["processor-id"])),
 
119
                          processor)
 
120
 
 
121
 
 
122
class PowerPCMessageFactory:
 
123
    """Factory for ppc-based processors provides processor information.
 
124
 
 
125
    @param source_filename: The file name of the data source.
 
126
    """
 
127
 
 
128
    def __init__(self, source_filename):
 
129
        self._source_filename = source_filename
 
130
 
 
131
    def create_message(self):
 
132
        """Returns a list containing information about each processor."""
 
133
        processors = []
 
134
        file = open(self._source_filename)
 
135
 
 
136
        try:
 
137
            current = None
 
138
 
 
139
            for line in file:
 
140
                parts = line.split(":", 1)
 
141
                key = parts[0].strip()
 
142
 
 
143
                if key == "processor":
 
144
                    current = {"processor-id": int(parts[1].strip())}
 
145
                    processors.append(current)
 
146
                elif key == "cpu":
 
147
                    current["model"] = parts[1].strip()
 
148
        finally:
 
149
            file.close()
 
150
 
 
151
        return processors
 
152
 
 
153
 
 
154
class ARMMessageFactory:
 
155
    """Factory for arm-based processors provides processor information.
 
156
 
 
157
    @param source_filename: The file name of the data source.
 
158
    """
 
159
 
 
160
    def __init__(self, source_filename):
 
161
        self._source_filename = source_filename
 
162
 
 
163
    def create_message(self):
 
164
        """Returns a list containing information about each processor."""
 
165
        processors = []
 
166
        file = open(self._source_filename)
 
167
 
 
168
        try:
 
169
            regexp = re.compile("(?P<key>.*?)\s*:\s*(?P<value>.*)")
 
170
            current = {}
 
171
 
 
172
            for line in file:
 
173
                match = regexp.match(line.strip())
 
174
                if match:
 
175
                    key = match.group("key")
 
176
                    value = match.group("value")
 
177
 
 
178
                    if key == "Processor":
 
179
                        # ARM doesn't support SMP, thus no processor-id in
 
180
                        # the cpuinfo
 
181
                        current["processor-id"] = 0
 
182
                        current["model"] = value
 
183
                    elif key == "Cache size":
 
184
                        current["cache-size"] = int(value)
 
185
 
 
186
            if current:
 
187
                processors.append(current)
 
188
        finally:
 
189
            file.close()
 
190
 
 
191
        return processors
 
192
 
 
193
 
 
194
class SparcMessageFactory:
 
195
    """Factory for sparc-based processors provides processor information.
 
196
 
 
197
    @param source_filename: The file name of the data source.
 
198
    """
 
199
 
 
200
    def __init__(self, source_filename):
 
201
        self._source_filename = source_filename
 
202
 
 
203
    def create_message(self):
 
204
        """Returns a list containing information about each processor."""
 
205
        processors = []
 
206
        model = None
 
207
        file = open(self._source_filename)
 
208
 
 
209
        try:
 
210
            regexp = re.compile("CPU(\d{1})+")
 
211
 
 
212
            for line in file:
 
213
                parts = line.split(":", 1)
 
214
                key = parts[0].strip()
 
215
 
 
216
                if key == "cpu":
 
217
                    model = parts[1].strip()
 
218
                elif regexp.match(key):
 
219
                    start, end = re.compile("\d+").search(key).span()
 
220
                    message = {"processor-id": int(key[start:end]),
 
221
                               "model": model}
 
222
                    processors.append(message)
 
223
        finally:
 
224
            file.close()
 
225
 
 
226
        return processors
 
227
 
 
228
 
 
229
class X86MessageFactory:
 
230
    """Factory for x86-based processors provides processor information.
 
231
 
 
232
    @param source_filename: The file name of the data source.
 
233
    """
 
234
 
 
235
    def __init__(self, source_filename):
 
236
        self._source_filename = source_filename
 
237
 
 
238
    def create_message(self):
 
239
        """Returns a list containing information about each processor."""
 
240
        processors = []
 
241
        file = open(self._source_filename)
 
242
 
 
243
        try:
 
244
            current = None
 
245
 
 
246
            for line in file:
 
247
                parts = line.split(":", 1)
 
248
                key = parts[0].strip()
 
249
 
 
250
                if key == "processor":
 
251
                    current = {"processor-id": int(parts[1].strip())}
 
252
                    processors.append(current)
 
253
                elif key == "vendor_id":
 
254
                    current["vendor"] = parts[1].strip()
 
255
                elif key == "model name":
 
256
                    current["model"] = parts[1].strip()
 
257
                elif key == "cache size":
 
258
                    value_parts = parts[1].split()
 
259
                    current["cache-size"] = int(value_parts[0].strip())
 
260
        finally:
 
261
            file.close()
 
262
 
 
263
        return processors
 
264
 
 
265
 
 
266
message_factories = [("(arm*|aarch64)", ARMMessageFactory),
 
267
                     ("ppc(64)?", PowerPCMessageFactory),
 
268
                     ("sparc[64]", SparcMessageFactory),
 
269
                     ("i[3-7]86|x86_64", X86MessageFactory)]