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

« back to all changes in this revision

Viewing changes to landscape/monitor/mountinfo.py

  • Committer: Bazaar Package Importer
  • Author(s): Rick Clark
  • Date: 2008-09-08 16:35:57 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080908163557-l3ixzj5dxz37wnw2
Tags: 1.0.18-0ubuntu1
New upstream release 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import time
 
2
import os
 
3
 
 
4
from landscape.lib.disk import get_mount_info
 
5
from landscape.lib.monitor import CoverageMonitor
 
6
from landscape.accumulate import Accumulator
 
7
from landscape.hal import HALManager
 
8
from landscape.monitor.monitor import MonitorPlugin
 
9
 
 
10
 
 
11
class MountInfo(MonitorPlugin):
 
12
 
 
13
    persist_name = "mount-info"
 
14
 
 
15
    def __init__(self, interval=300, monitor_interval=60*60,
 
16
                 mounts_file="/proc/mounts", create_time=time.time,
 
17
                 statvfs=None, hal_manager=None, mtab_file="/etc/mtab"):
 
18
        self.run_interval = interval
 
19
        self._monitor_interval = monitor_interval
 
20
        self._create_time = create_time
 
21
        self._mounts_file = mounts_file
 
22
        self._mtab_file = mtab_file
 
23
        if statvfs is None:
 
24
            statvfs = os.statvfs
 
25
        self._statvfs = statvfs
 
26
        self._create_time = create_time
 
27
        self._free_space = []
 
28
        self._mount_info = []
 
29
        self._mount_activity = []
 
30
        self._prev_mount_activity = {}
 
31
        self._hal_manager = hal_manager or HALManager()
 
32
 
 
33
    def register(self, registry):
 
34
        super(MountInfo, self).register(registry)
 
35
        self._accumulate = Accumulator(self._persist, self.registry.step_size)
 
36
        self._monitor = CoverageMonitor(self.run_interval, 0.8,
 
37
                                        "mount info snapshot",
 
38
                                        create_time=self._create_time)
 
39
        self.registry.reactor.call_every(self._monitor_interval,
 
40
                                         self._monitor.log)
 
41
        self.registry.reactor.call_on("stop", self._monitor.log, priority=2000)
 
42
        self.registry.reactor.call_on("resynchronize", self._resynchronize)
 
43
        self.call_on_accepted("mount-info", self.send_messages, True)
 
44
 
 
45
    def _resynchronize(self):
 
46
        self.registry.persist.remove(self.persist_name)
 
47
 
 
48
    def create_messages(self):
 
49
        return filter(None, [self.create_mount_info_message(),
 
50
                             self.create_free_space_message(),
 
51
                             self.create_mount_activity_message()])
 
52
 
 
53
    def create_mount_activity_message(self):
 
54
        if self._mount_activity:
 
55
            message = {"type": "mount-activity",
 
56
                       "activities": self._mount_activity}
 
57
            self._mount_activity = []
 
58
            return message
 
59
        return None
 
60
 
 
61
    def create_mount_info_message(self):
 
62
        if self._mount_info:
 
63
            message = {"type": "mount-info", "mount-info": self._mount_info}
 
64
            self._mount_info = []
 
65
            return message
 
66
        return None
 
67
 
 
68
    def create_free_space_message(self):
 
69
        if self._free_space:
 
70
            message = {"type": "free-space", "free-space": self._free_space}
 
71
            self._free_space = []
 
72
            return message
 
73
        return None
 
74
 
 
75
    def send_messages(self, urgent=False):
 
76
        for message in self.create_messages():
 
77
            self.registry.broker.send_message(message, urgent=urgent)
 
78
 
 
79
    def exchange(self):
 
80
        self.registry.broker.call_if_accepted("mount-info",
 
81
                                              self.send_messages)
 
82
 
 
83
    def run(self):
 
84
        self._monitor.ping()
 
85
        now = int(self._create_time())
 
86
        current_mount_points = set()
 
87
        for mount_info in self._get_mount_info():
 
88
            mount_point = mount_info["mount-point"]
 
89
            free_space = mount_info.pop("free-space")
 
90
 
 
91
            key = ("accumulate-free-space", mount_point)
 
92
            step_data = self._accumulate(now, free_space, key)
 
93
            if step_data:
 
94
                timestamp = step_data[0]
 
95
                free_space = int(step_data[1])
 
96
                self._free_space.append((timestamp, mount_point, free_space))
 
97
 
 
98
            prev_mount_info = self._persist.get(("mount-info", mount_point))
 
99
            if not prev_mount_info or prev_mount_info != mount_info:
 
100
                self._persist.set(("mount-info", mount_point), mount_info)
 
101
                self._mount_info.append((now, mount_info))
 
102
 
 
103
            if not self._prev_mount_activity.get(mount_point, False):
 
104
                self._mount_activity.append((now, mount_point, True))
 
105
                self._prev_mount_activity[mount_point] = True
 
106
 
 
107
            current_mount_points.add(mount_point)
 
108
 
 
109
        for mount_point in self._prev_mount_activity:
 
110
            if mount_point not in current_mount_points:
 
111
                self._mount_activity.append((now, mount_point, False))
 
112
                self._prev_mount_activity[mount_point] = False
 
113
 
 
114
    def _get_removable_devices(self):
 
115
        block_devices = {} # {udi: [device, ...]}
 
116
        children = {} # {parent_udi: [child_udi, ...]}
 
117
        removable = set()
 
118
 
 
119
        # We walk the list of devices building up a dictionary of all removable
 
120
        # devices, and a mapping of {UDI => [block devices]}
 
121
        # We differentiate between devices that we definitely know are
 
122
        # removable and devices that _may_ be removable, depending on their
 
123
        # parent device, e.g. /dev/sdb1 isn't flagged as removable, but
 
124
        # /dev/sdb may well be removable.
 
125
 
 
126
        # Unfortunately, HAL doesn't guarantee the order of the devices
 
127
        # returned from get_devices(), so we may not know that a parent device
 
128
        # is removable when we find it's first child.
 
129
        devices = self._hal_manager.get_devices()
 
130
        for device in devices:
 
131
            block_device = device.properties.get("block.device")
 
132
            if block_device:
 
133
                if device.properties.get("storage.removable"):
 
134
                    removable.add(device.udi)
 
135
 
 
136
                try:
 
137
                    block_devices[device.udi].append(block_device)
 
138
                except KeyError:
 
139
                    block_devices[device.udi] = [block_device]
 
140
 
 
141
                parent_udi = device.properties.get("info.parent")
 
142
                if parent_udi is not None:
 
143
                    try:
 
144
                        children[parent_udi].append(device.udi)
 
145
                    except KeyError:
 
146
                        children[parent_udi] = [device.udi]
 
147
 
 
148
        # Propagate the removable flag from each node all the way to
 
149
        # its leaf children.
 
150
        updated = True
 
151
        while updated:
 
152
            updated = False
 
153
            for parent_udi in children:
 
154
                if parent_udi in removable:
 
155
                    for child_udi in children[parent_udi]:
 
156
                        if child_udi not in removable:
 
157
                            removable.add(child_udi)
 
158
                            updated = True
 
159
 
 
160
        # We've now seen _all_ devices, and have the definitive list of
 
161
        # removable UDIs, so we can now find all the removable devices in the
 
162
        # system.
 
163
        removable_devices = set()
 
164
        for udi in removable:
 
165
            removable_devices.update(block_devices[udi])
 
166
 
 
167
        return removable_devices
 
168
 
 
169
    def _get_mount_info(self):
 
170
        """Generator yields local mount points worth recording data for."""
 
171
        file = open(self._mounts_file, "r")
 
172
        removable_devices = self._get_removable_devices()
 
173
        bound_mount_points = self._get_bound_mount_points()
 
174
 
 
175
        for info in get_mount_info(self._mounts_file, self._statvfs):
 
176
            device = info["device"]
 
177
            mount_point = info["mount-point"]
 
178
            if (device.startswith("/dev/") and
 
179
                not mount_point.startswith("/dev/") and
 
180
                not device in removable_devices and
 
181
                not mount_point in bound_mount_points):
 
182
                yield info
 
183
 
 
184
    def _get_bound_mount_points(self):
 
185
        """
 
186
        Returns a set of mount points that have the "bind" option
 
187
        by parsing /etc/mtab.
 
188
        """
 
189
        bound_points = set()
 
190
        if not self._mtab_file or not os.path.isfile(self._mtab_file):
 
191
            return bound_points
 
192
 
 
193
        file = open(self._mtab_file, "r")
 
194
        for line in file:
 
195
            try:
 
196
                device, mount_point, filesystem, options = line.split()[:4]
 
197
                mount_point = mount_point.decode("string-escape")
 
198
            except ValueError:
 
199
                continue
 
200
            if "bind" in options.split(","):
 
201
                bound_points.add(mount_point)
 
202
        return bound_points
 
203