~ahasenack/landscape-client/landscape-client-1.5.5-0ubuntu0.9.04.0

« back to all changes in this revision

Viewing changes to landscape/monitor/processorinfo.py

  • Committer: Bazaar Package Importer
  • Author(s): Rick Clark
  • Date: 2008-09-08 16:35:57 UTC
  • mto: This revision was merged to the branch mainline in revision 2.
  • Revision ID: james.westby@ubuntu.com-20080908163557-fl0d2oc35hur473w
Tags: upstream-1.0.18
ImportĀ upstreamĀ versionĀ 1.0.18

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.monitor 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
 
 
31
    persist_name = "processor-info"
 
32
    # Prevent the Plugin base-class from scheduling looping calls.
 
33
    run_interval = None
 
34
 
 
35
    def __init__(self, delay=2, machine_name=None,
 
36
                 source_filename="/proc/cpuinfo"):
 
37
        """Initialize plugin with starting delay and source filename."""
 
38
        self._delay = delay
 
39
        self._source_filename = source_filename
 
40
 
 
41
        if machine_name is None:
 
42
            machine_name = os.uname()[4]
 
43
 
 
44
        self._cpu_info_reader = self._create_cpu_info_reader(machine_name,
 
45
                                                             source_filename)
 
46
 
 
47
    def _create_cpu_info_reader(self, machine_name, source_filename):
 
48
        """Return a message factory suitable for the specified machine name."""
 
49
        for pair in message_factories:
 
50
            regexp = re.compile(pair[0])
 
51
 
 
52
            if regexp.match(machine_name):
 
53
                return pair[1](source_filename)
 
54
 
 
55
        raise PluginConfigError("A processor info reader for '%s' is not "
 
56
                                "available." % machine_name)
 
57
 
 
58
    def register(self, registry):
 
59
        """Register this plugin with the specified plugin registry."""
 
60
        super(ProcessorInfo, self).register(registry)
 
61
        self.registry.reactor.call_later(self._delay, self.run)
 
62
        self.registry.reactor.call_on("resynchronize", self._resynchronize)
 
63
        self.call_on_accepted("processor-info", self.send_message, True)
 
64
 
 
65
    def _resynchronize(self):
 
66
        self.registry.persist.remove(self.persist_name)
 
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(message, urgent=urgent)
 
92
 
 
93
    def run(self, urgent=False):
 
94
        """Create a message and put it on the message queue."""
 
95
        self.registry.broker.call_if_accepted("processor-info",
 
96
                                              self.send_message, urgent)
 
97
 
 
98
    def _has_changed(self, processor, message):
 
99
        """Returns true if processor details changed since the last read."""
 
100
        if processor["model"] != message["model"]:
 
101
            return True
 
102
 
 
103
        if processor["vendor"] != message.get("vendor", ""):
 
104
            return True
 
105
 
 
106
        if processor["cache_size"] != message.get("cache-size", -1):
 
107
            return True
 
108
 
 
109
        return False
 
110
 
 
111
    def _update(self, processor, message):
 
112
        """Update the processor details with current values."""
 
113
        processor["id"] = message["processor-id"]
 
114
        processor["model"] = message["model"]
 
115
        processor["cache_size"] = message.get("cache-size", -1)
 
116
        processor["vendor"] = message.get("vendor", "")
 
117
        self._persist.set(("processor", str(message["processor-id"])),
 
118
                          processor)
 
119
 
 
120
 
 
121
class PowerPCMessageFactory:
 
122
    """Factory for ppc-based processors provides processor information."""
 
123
 
 
124
    def __init__(self, source_filename):
 
125
        """Initialize reader with filename of data source."""
 
126
        self._source_filename = source_filename
 
127
 
 
128
    def create_message(self):
 
129
        """Returns a list containing information about each processor."""
 
130
        processors = []
 
131
        file = open(self._source_filename)
 
132
 
 
133
        try:
 
134
            current = None
 
135
 
 
136
            for line in file:
 
137
                parts = line.split(":", 1)
 
138
                key = parts[0].strip()
 
139
 
 
140
                if key == "processor":
 
141
                    current = {"processor-id": int(parts[1].strip())}
 
142
                    processors.append(current)
 
143
                elif key == "cpu":
 
144
                    current["model"] = parts[1].strip()
 
145
        finally:
 
146
            file.close()
 
147
 
 
148
        return processors
 
149
 
 
150
 
 
151
class SparcMessageFactory:
 
152
    """Factory for sparc-based processors provides processor information."""
 
153
 
 
154
    def __init__(self, source_filename):
 
155
        """Initialize reader with filename of data source."""
 
156
        self._source_filename = source_filename
 
157
 
 
158
    def create_message(self):
 
159
        """Returns a list containing information about each processor."""
 
160
        processors = []
 
161
        model = None
 
162
        file = open(self._source_filename)
 
163
 
 
164
        try:
 
165
            regexp = re.compile("CPU(\d{1})+")
 
166
 
 
167
            for line in file:
 
168
                parts = line.split(":", 1)
 
169
                key = parts[0].strip()
 
170
 
 
171
                if key == "cpu":
 
172
                    model = parts[1].strip()
 
173
                elif regexp.match(key):
 
174
                    start, end = re.compile("\d+").search(key).span()
 
175
                    message = {"processor-id": int(key[start:end]),
 
176
                               "model": model}
 
177
                    processors.append(message)
 
178
        finally:
 
179
            file.close()
 
180
 
 
181
        return processors
 
182
 
 
183
 
 
184
class X86MessageFactory:
 
185
    """Factory for x86-based processors provides processor information."""
 
186
 
 
187
    def __init__(self, source_filename):
 
188
        """Initialize reader with filename of data source."""
 
189
        self._source_filename = source_filename
 
190
 
 
191
    def create_message(self):
 
192
        """Returns a list containing information about each processor."""
 
193
        processors = []
 
194
        file = open(self._source_filename)
 
195
 
 
196
        try:
 
197
            current = None
 
198
 
 
199
            for line in file:
 
200
                parts = line.split(":", 1)
 
201
                key = parts[0].strip()
 
202
 
 
203
                if key == "processor":
 
204
                    current = {"processor-id": int(parts[1].strip())}
 
205
                    processors.append(current)
 
206
                elif key == "vendor_id":
 
207
                    current["vendor"] = parts[1].strip()
 
208
                elif key == "model name":
 
209
                    current["model"] = parts[1].strip()
 
210
                elif key == "cache size":
 
211
                    value_parts = parts[1].split()
 
212
                    current["cache-size"] = int(value_parts[0].strip())
 
213
        finally:
 
214
            file.close()
 
215
 
 
216
        return processors
 
217
 
 
218
 
 
219
message_factories = [("ppc(64)?", PowerPCMessageFactory),
 
220
                     ("sparc[64]", SparcMessageFactory),
 
221
                     ("i[3-7]86|x86_64", X86MessageFactory)]