5
from landscape.plugin import PluginConfigError
6
from landscape.monitor.plugin import MonitorPlugin
9
class ProcessorInfo(MonitorPlugin):
10
"""Plugin captures information about the processor(s) in this machine.
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.
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
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.
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.
35
persist_name = "processor-info"
37
# Prevent the Plugin base-class from scheduling looping calls.
40
def __init__(self, delay=2, machine_name=None,
41
source_filename="/proc/cpuinfo"):
43
self._source_filename = source_filename
45
if machine_name is None:
46
machine_name = os.uname()[4]
48
self._cpu_info_reader = self._create_cpu_info_reader(machine_name,
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])
56
if regexp.match(machine_name):
57
return pair[1](source_filename)
59
raise PluginConfigError("A processor info reader for '%s' is not "
60
"available." % machine_name)
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)
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()}
73
def send_message(self, urgent=False):
75
message = self.create_message()
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:
82
self._update(cached_processor, processor)
85
if self._has_changed(cached_processor, processor):
86
self._update(cached_processor, processor)
90
logging.info("Queueing message with updated processor info.")
91
self.registry.broker.send_message(
92
message, self._session_id, urgent=urgent)
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)
99
def _has_changed(self, processor, message):
100
"""Returns true if processor details changed since the last read."""
101
if processor["model"] != message["model"]:
104
if processor["vendor"] != message.get("vendor", ""):
107
if processor["cache_size"] != message.get("cache-size", -1):
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"])),
122
class PowerPCMessageFactory:
123
"""Factory for ppc-based processors provides processor information.
125
@param source_filename: The file name of the data source.
128
def __init__(self, source_filename):
129
self._source_filename = source_filename
131
def create_message(self):
132
"""Returns a list containing information about each processor."""
134
file = open(self._source_filename)
140
parts = line.split(":", 1)
141
key = parts[0].strip()
143
if key == "processor":
144
current = {"processor-id": int(parts[1].strip())}
145
processors.append(current)
147
current["model"] = parts[1].strip()
154
class ARMMessageFactory:
155
"""Factory for arm-based processors provides processor information.
157
@param source_filename: The file name of the data source.
160
def __init__(self, source_filename):
161
self._source_filename = source_filename
163
def create_message(self):
164
"""Returns a list containing information about each processor."""
166
file = open(self._source_filename)
169
regexp = re.compile("(?P<key>.*?)\s*:\s*(?P<value>.*)")
173
match = regexp.match(line.strip())
175
key = match.group("key")
176
value = match.group("value")
178
if key == "Processor":
179
# ARM doesn't support SMP, thus no processor-id in
181
current["processor-id"] = 0
182
current["model"] = value
183
elif key == "Cache size":
184
current["cache-size"] = int(value)
187
processors.append(current)
194
class SparcMessageFactory:
195
"""Factory for sparc-based processors provides processor information.
197
@param source_filename: The file name of the data source.
200
def __init__(self, source_filename):
201
self._source_filename = source_filename
203
def create_message(self):
204
"""Returns a list containing information about each processor."""
207
file = open(self._source_filename)
210
regexp = re.compile("CPU(\d{1})+")
213
parts = line.split(":", 1)
214
key = parts[0].strip()
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]),
222
processors.append(message)
229
class X86MessageFactory:
230
"""Factory for x86-based processors provides processor information.
232
@param source_filename: The file name of the data source.
235
def __init__(self, source_filename):
236
self._source_filename = source_filename
238
def create_message(self):
239
"""Returns a list containing information about each processor."""
241
file = open(self._source_filename)
247
parts = line.split(":", 1)
248
key = parts[0].strip()
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())
266
message_factories = [("(arm*|aarch64)", ARMMessageFactory),
267
("ppc(64)?", PowerPCMessageFactory),
268
("sparc[64]", SparcMessageFactory),
269
("i[3-7]86|x86_64", X86MessageFactory)]