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
215
216
217
218
219
220
221
|
import logging
import os
import re
from landscape.plugin import PluginConfigError
from landscape.monitor.monitor import MonitorPlugin
class ProcessorInfo(MonitorPlugin):
"""Plugin captures information about the processor(s) in this machine.
This plugin runs once per client session. When processor
information is retrieved it's compared against the last known
processor information, which is saved in persistent storage. A
message is only put on the message queue if the latest processor
information differs from the last known processor information.
The information available from /proc/cpuinfo varies per platform.
For example, an Apple PowerMac Dual G5 doesn't contain a vendor ID
and provides the processor name in the 'cpu' field, as opposed to
the 'model name' field used on x86-based hardware. For reasons
such as this, the schema of the data reported by this plugin is
flexible. Only 'processor-id' and 'model' are guaranteed to be
present.
In order to deal with the vagaries of parsing /proc/cpu
information on the various platforms we support, message
generation is deferred to per-platform message factories.
"""
persist_name = "processor-info"
# Prevent the Plugin base-class from scheduling looping calls.
run_interval = None
def __init__(self, delay=2, machine_name=None,
source_filename="/proc/cpuinfo"):
"""Initialize plugin with starting delay and source filename."""
self._delay = delay
self._source_filename = source_filename
if machine_name is None:
machine_name = os.uname()[4]
self._cpu_info_reader = self._create_cpu_info_reader(machine_name,
source_filename)
def _create_cpu_info_reader(self, machine_name, source_filename):
"""Return a message factory suitable for the specified machine name."""
for pair in message_factories:
regexp = re.compile(pair[0])
if regexp.match(machine_name):
return pair[1](source_filename)
raise PluginConfigError("A processor info reader for '%s' is not "
"available." % machine_name)
def register(self, registry):
"""Register this plugin with the specified plugin registry."""
super(ProcessorInfo, self).register(registry)
self.registry.reactor.call_later(self._delay, self.run)
self.registry.reactor.call_on("resynchronize", self._resynchronize)
self.call_on_accepted("processor-info", self.send_message, True)
def _resynchronize(self):
self.registry.persist.remove(self.persist_name)
def create_message(self):
"""Retrieve processor information and generate a message."""
return {"type": "processor-info",
"processors": self._cpu_info_reader.create_message()}
def send_message(self, urgent=False):
dirty = False
message = self.create_message()
for processor in message["processors"]:
key = ("processor", str(processor["processor-id"]))
cached_processor = self._persist.get(key)
if cached_processor is None:
cached_processor = {}
self._update(cached_processor, processor)
dirty = True
else:
if self._has_changed(cached_processor, processor):
self._update(cached_processor, processor)
dirty = True
if dirty:
logging.info("Queueing message with updated processor info.")
self.registry.broker.send_message(message, urgent=urgent)
def run(self, urgent=False):
"""Create a message and put it on the message queue."""
self.registry.broker.call_if_accepted("processor-info",
self.send_message, urgent)
def _has_changed(self, processor, message):
"""Returns true if processor details changed since the last read."""
if processor["model"] != message["model"]:
return True
if processor["vendor"] != message.get("vendor", ""):
return True
if processor["cache_size"] != message.get("cache-size", -1):
return True
return False
def _update(self, processor, message):
"""Update the processor details with current values."""
processor["id"] = message["processor-id"]
processor["model"] = message["model"]
processor["cache_size"] = message.get("cache-size", -1)
processor["vendor"] = message.get("vendor", "")
self._persist.set(("processor", str(message["processor-id"])),
processor)
class PowerPCMessageFactory:
"""Factory for ppc-based processors provides processor information."""
def __init__(self, source_filename):
"""Initialize reader with filename of data source."""
self._source_filename = source_filename
def create_message(self):
"""Returns a list containing information about each processor."""
processors = []
file = open(self._source_filename)
try:
current = None
for line in file:
parts = line.split(":", 1)
key = parts[0].strip()
if key == "processor":
current = {"processor-id": int(parts[1].strip())}
processors.append(current)
elif key == "cpu":
current["model"] = parts[1].strip()
finally:
file.close()
return processors
class SparcMessageFactory:
"""Factory for sparc-based processors provides processor information."""
def __init__(self, source_filename):
"""Initialize reader with filename of data source."""
self._source_filename = source_filename
def create_message(self):
"""Returns a list containing information about each processor."""
processors = []
model = None
file = open(self._source_filename)
try:
regexp = re.compile("CPU(\d{1})+")
for line in file:
parts = line.split(":", 1)
key = parts[0].strip()
if key == "cpu":
model = parts[1].strip()
elif regexp.match(key):
start, end = re.compile("\d+").search(key).span()
message = {"processor-id": int(key[start:end]),
"model": model}
processors.append(message)
finally:
file.close()
return processors
class X86MessageFactory:
"""Factory for x86-based processors provides processor information."""
def __init__(self, source_filename):
"""Initialize reader with filename of data source."""
self._source_filename = source_filename
def create_message(self):
"""Returns a list containing information about each processor."""
processors = []
file = open(self._source_filename)
try:
current = None
for line in file:
parts = line.split(":", 1)
key = parts[0].strip()
if key == "processor":
current = {"processor-id": int(parts[1].strip())}
processors.append(current)
elif key == "vendor_id":
current["vendor"] = parts[1].strip()
elif key == "model name":
current["model"] = parts[1].strip()
elif key == "cache size":
value_parts = parts[1].split()
current["cache-size"] = int(value_parts[0].strip())
finally:
file.close()
return processors
message_factories = [("ppc(64)?", PowerPCMessageFactory),
("sparc[64]", SparcMessageFactory),
("i[3-7]86|x86_64", X86MessageFactory)]
|