Merge lp:~therve/landscape-client/restore-hardware-monitor into lp:~landscape/landscape-client/trunk
- restore-hardware-monitor
- Merge into trunk
Proposed by
Thomas Herve
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Geoff Teale | ||||
Approved revision: | 471 | ||||
Merged at revision: | 473 | ||||
Proposed branch: | lp:~therve/landscape-client/restore-hardware-monitor | ||||
Merge into: | lp:~landscape/landscape-client/trunk | ||||
Diff against target: |
1516 lines (+1060/-27) 32 files modified
README (+10/-1) dbus/landscape.conf (+62/-0) debian/landscape-client.install (+2/-0) debian/landscape-client.postinst (+11/-0) debian/rules (+9/-6) landscape/broker/exchange.py (+3/-1) landscape/hal.py (+52/-0) landscape/lib/bpickle_dbus.py (+65/-0) landscape/monitor/config.py (+5/-4) landscape/monitor/hardwareinventory.py (+114/-0) landscape/monitor/mountinfo.py (+65/-2) landscape/monitor/tests/test_hardwareinventory.py (+273/-0) landscape/monitor/tests/test_mountinfo.py (+76/-0) landscape/monitor/tests/test_service.py (+9/-3) landscape/package/releaseupgrader.py (+21/-0) landscape/package/tests/test_releaseupgrader.py (+54/-0) landscape/reactor.py (+11/-0) landscape/service.py (+6/-0) landscape/tests/test_configuration.py (+1/-1) landscape/tests/test_hal.py (+87/-0) landscape/tests/test_service.py (+9/-0) landscape/tests/test_textmessage.py (+2/-2) landscape/textmessage.py (+2/-1) landscape/watchdog.py (+5/-2) man/landscape-client.1 (+6/-1) man/landscape-client.txt (+2/-0) man/landscape-config.1 (+6/-1) man/landscape-config.txt (+2/-0) man/landscape-message.1 (+5/-1) man/landscape-message.txt (+1/-0) scripts/landscape-dbus-proxy (+82/-0) setup.py (+2/-1) |
||||
To merge this branch: | bzr merge lp:~therve/landscape-client/restore-hardware-monitor | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Geoff Teale (community) | Approve | ||
Fernando Correa Neto (community) | Approve | ||
Review via email:
|
Commit message
Description of the change
The branch basically reverts r414 from trunk, except keeping the new hardwareinfo manager plugin.
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Geoff Teale (tealeg) wrote : | # |
+1 Good for me.
One small thing.
+ logging.
The "to" there is unecessary.
review:
Approve
- 472. By Thomas Herve
-
CLeanups
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README' | |||
2 | --- README 2011-12-01 14:01:29 +0000 | |||
3 | +++ README 2012-03-06 09:20:24 +0000 | |||
4 | @@ -23,6 +23,14 @@ | |||
5 | 23 | 23 | ||
6 | 24 | == Developing == | 24 | == Developing == |
7 | 25 | 25 | ||
8 | 26 | To run the full test suite, you must have a dbus session bus | ||
9 | 27 | running. If you don't have one (for example, if you're running the | ||
10 | 28 | tests in an ssh session), run the following command: | ||
11 | 29 | |||
12 | 30 | export DBUS_SESSION_BUS_ADDRESS=`dbus-daemon --print-address=1 --session --fork` | ||
13 | 31 | |||
14 | 32 | Then your tests should pass. | ||
15 | 33 | |||
16 | 26 | When you want to test the landscape client manually without management | 34 | When you want to test the landscape client manually without management |
17 | 27 | features, you can simply run: | 35 | features, you can simply run: |
18 | 28 | 36 | ||
19 | @@ -31,7 +39,8 @@ | |||
20 | 31 | This defaults to the 'landscape-client.conf' configuration file. | 39 | This defaults to the 'landscape-client.conf' configuration file. |
21 | 32 | 40 | ||
22 | 33 | When you want to test management features manually, you'll need to run as root. | 41 | When you want to test management features manually, you'll need to run as root. |
24 | 34 | There's a configuration file 'root-client.conf'. | 42 | There's a configuration file 'root-client.conf' which specifies use of the |
25 | 43 | system bus. | ||
26 | 35 | 44 | ||
27 | 36 | $ sudo ./scripts/landscape-client -c root-client.conf | 45 | $ sudo ./scripts/landscape-client -c root-client.conf |
28 | 37 | 46 | ||
29 | 38 | 47 | ||
30 | === added directory 'dbus' | |||
31 | === added file 'dbus/landscape.conf' | |||
32 | --- dbus/landscape.conf 1970-01-01 00:00:00 +0000 | |||
33 | +++ dbus/landscape.conf 2012-03-06 09:20:24 +0000 | |||
34 | @@ -0,0 +1,62 @@ | |||
35 | 1 | <!DOCTYPE busconfig PUBLIC | ||
36 | 2 | "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" | ||
37 | 3 | "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> | ||
38 | 4 | <busconfig> | ||
39 | 5 | |||
40 | 6 | <policy user="landscape"> | ||
41 | 7 | <allow own="com.canonical.landscape.Broker" /> | ||
42 | 8 | <allow own="com.canonical.landscape.Monitor" /> | ||
43 | 9 | |||
44 | 10 | <allow send_destination="com.canonical.landscape.Broker" /> | ||
45 | 11 | <allow receive_sender="com.canonical.landscape.Broker" /> | ||
46 | 12 | |||
47 | 13 | <allow send_destination="com.canonical.landscape.Monitor" /> | ||
48 | 14 | <allow receive_sender="com.canonical.landscape.Monitor" /> | ||
49 | 15 | |||
50 | 16 | <allow send_destination="com.canonical.landscape.Manager" /> | ||
51 | 17 | <allow receive_sender="com.canonical.landscape.Manager" /> | ||
52 | 18 | |||
53 | 19 | <allow send_interface="org.freedesktop.Hal.Manager" /> | ||
54 | 20 | <allow send_interface="org.freedesktop.Hal.Device" /> | ||
55 | 21 | |||
56 | 22 | </policy> | ||
57 | 23 | |||
58 | 24 | <!-- this is a horrible hack --> | ||
59 | 25 | <policy user="haldaemon"> | ||
60 | 26 | |||
61 | 27 | <allow receive_sender="com.canonical.landscape.Manager" /> | ||
62 | 28 | <allow receive_sender="com.canonical.landscape.Monitor" /> | ||
63 | 29 | <allow receive_sender="com.canonical.landscape.Broker" /> | ||
64 | 30 | |||
65 | 31 | </policy> | ||
66 | 32 | |||
67 | 33 | <policy user="root"> | ||
68 | 34 | <allow own="com.canonical.landscape.Manager" /> | ||
69 | 35 | |||
70 | 36 | <allow send_destination="com.canonical.landscape.Broker" /> | ||
71 | 37 | <allow receive_sender="com.canonical.landscape.Broker" /> | ||
72 | 38 | |||
73 | 39 | <allow send_destination="com.canonical.landscape.Monitor" /> | ||
74 | 40 | <allow receive_sender="com.canonical.landscape.Monitor" /> | ||
75 | 41 | |||
76 | 42 | <allow send_destination="com.canonical.landscape.Manager" /> | ||
77 | 43 | <allow receive_sender="com.canonical.landscape.Manager" /> | ||
78 | 44 | </policy> | ||
79 | 45 | |||
80 | 46 | <policy context="default"> | ||
81 | 47 | <deny own="com.canonical.landscape.Broker" /> | ||
82 | 48 | <deny own="com.canonical.landscape.Monitor" /> | ||
83 | 49 | <deny own="com.canonical.landscape.Manager" /> | ||
84 | 50 | |||
85 | 51 | <deny send_destination="com.canonical.landscape.Broker" /> | ||
86 | 52 | <deny receive_sender="com.canonical.landscape.Broker" /> | ||
87 | 53 | |||
88 | 54 | <deny send_destination="com.canonical.landscape.Monitor" /> | ||
89 | 55 | <deny receive_sender="com.canonical.landscape.Monitor" /> | ||
90 | 56 | |||
91 | 57 | <deny send_destination="com.canonical.landscape.Manager" /> | ||
92 | 58 | <deny receive_sender="com.canonical.landscape.Manager" /> | ||
93 | 59 | |||
94 | 60 | </policy> | ||
95 | 61 | |||
96 | 62 | </busconfig> | ||
97 | 0 | 63 | ||
98 | === modified file 'debian/landscape-client.install' | |||
99 | --- debian/landscape-client.install 2011-12-01 14:01:29 +0000 | |||
100 | +++ debian/landscape-client.install 2012-03-06 09:20:24 +0000 | |||
101 | @@ -8,5 +8,7 @@ | |||
102 | 8 | usr/bin/landscape-package-reporter | 8 | usr/bin/landscape-package-reporter |
103 | 9 | usr/bin/landscape-release-upgrader | 9 | usr/bin/landscape-release-upgrader |
104 | 10 | usr/bin/landscape-is-cloud-managed | 10 | usr/bin/landscape-is-cloud-managed |
105 | 11 | usr/bin/landscape-dbus-proxy | ||
106 | 11 | usr/share/landscape/cloud-default.conf | 12 | usr/share/landscape/cloud-default.conf |
107 | 13 | etc/dbus-1/system.d/landscape.conf | ||
108 | 12 | usr/lib/landscape | 14 | usr/lib/landscape |
109 | 13 | 15 | ||
110 | === modified file 'debian/landscape-client.postinst' | |||
111 | --- debian/landscape-client.postinst 2012-02-28 17:47:57 +0000 | |||
112 | +++ debian/landscape-client.postinst 2012-03-06 09:20:24 +0000 | |||
113 | @@ -129,6 +129,17 @@ | |||
114 | 129 | if [ -e $very_old_cron_job ]; then | 129 | if [ -e $very_old_cron_job ]; then |
115 | 130 | rm $very_old_cron_job | 130 | rm $very_old_cron_job |
116 | 131 | fi | 131 | fi |
117 | 132 | |||
118 | 133 | # Check if we're upgrading from a D-Bus version | ||
119 | 134 | if ! [ -z $2 ]; then | ||
120 | 135 | if dpkg --compare-versions $2 lt 1.5.1; then | ||
121 | 136 | # Launch a proxy service that will forward requests over DBus | ||
122 | 137 | # from the old package-changer to the new AMP-based broker. This | ||
123 | 138 | # is a one-off only needed for the DBus->AMP upgrade | ||
124 | 139 | start-stop-daemon -x /usr/bin/landscape-dbus-proxy -b -c landscape -u landscape -S | ||
125 | 140 | fi | ||
126 | 141 | fi | ||
127 | 142 | |||
128 | 132 | ;; | 143 | ;; |
129 | 133 | 144 | ||
130 | 134 | abort-upgrade|abort-remove|abort-deconfigure) | 145 | abort-upgrade|abort-remove|abort-deconfigure) |
131 | 135 | 146 | ||
132 | === modified file 'debian/rules' | |||
133 | --- debian/rules 2012-02-10 17:59:13 +0000 | |||
134 | +++ debian/rules 2012-03-06 09:20:24 +0000 | |||
135 | @@ -60,6 +60,7 @@ | |||
136 | 60 | install -D -o root -g root -m 644 debian/cloud-default.conf $(root_dir)/usr/share/landscape/cloud-default.conf | 60 | install -D -o root -g root -m 644 debian/cloud-default.conf $(root_dir)/usr/share/landscape/cloud-default.conf |
137 | 61 | install -D -o root -g root -m 755 smart-update/smart-update $(root_dir)/usr/lib/landscape/smart-update | 61 | install -D -o root -g root -m 755 smart-update/smart-update $(root_dir)/usr/lib/landscape/smart-update |
138 | 62 | install -D -o root -g root -m 755 apt-update/apt-update $(root_dir)/usr/lib/landscape/apt-update | 62 | install -D -o root -g root -m 755 apt-update/apt-update $(root_dir)/usr/lib/landscape/apt-update |
139 | 63 | install -D -o root -g root -m 644 dbus/landscape.conf $(root_dir)/etc/dbus-1/system.d/landscape.conf | ||
140 | 63 | 64 | ||
141 | 64 | binary-indep: | 65 | binary-indep: |
142 | 65 | # do nothing | 66 | # do nothing |
143 | @@ -83,22 +84,24 @@ | |||
144 | 83 | 84 | ||
145 | 84 | ifneq (,$(findstring $(dist_release),"dapper")) | 85 | ifneq (,$(findstring $(dist_release),"dapper")) |
146 | 85 | # We need python2.4-pysqlite2 and a non-buggy libcurl3-gnutls on dapper | 86 | # We need python2.4-pysqlite2 and a non-buggy libcurl3-gnutls on dapper |
149 | 86 | echo "extra:Depends=python2.4-pysqlite2, libcurl3-gnutls (>= 7.15.1-1ubuntu3), python-smartpm (>= 1.1.1~bzr20081010-0ubuntu1.6.06.0)" >> $(landscape_common_substvars) | 87 | echo "extra:Depends=python2.4-pysqlite2, libcurl3-gnutls (>= 7.15.1-1ubuntu3), python-smartpm (>= 1.1.1~bzr20081010-0ubuntu1.6.06.0), python2.4-dbus" >> $(landscape_common_substvars) |
150 | 87 | echo "extra:Depends=python2.4-pycurl" >> $(landscape_client_substvars) | 88 | echo "extra:Depends=python2.4-pycurl, hal" >> $(landscape_client_substvars) |
151 | 88 | endif | 89 | endif |
152 | 89 | ifneq (,$(findstring $(dist_release),"hardy")) | 90 | ifneq (,$(findstring $(dist_release),"hardy")) |
153 | 90 | # We want the smart 1.1.1 from the Landscape repository on hardy | 91 | # We want the smart 1.1.1 from the Landscape repository on hardy |
156 | 91 | echo "extra:Depends=python-smartpm (>= 1.1.1~bzr20081010-0ubuntu1.8.04.1)" >> $(landscape_common_substvars) | 92 | echo "extra:Depends=python-smartpm (>= 1.1.1~bzr20081010-0ubuntu1.8.04.1), python-dbus" >> $(landscape_common_substvars) |
157 | 92 | echo "extra:Depends=python-pycurl" >> $(landscape_client_substvars) | 93 | echo "extra:Depends=python-pycurl, hal" >> $(landscape_client_substvars) |
158 | 93 | endif | 94 | endif |
159 | 94 | ifneq (,$(filter $(dist_release),karmic lucid maverick)) | 95 | ifneq (,$(filter $(dist_release),karmic lucid maverick)) |
160 | 95 | # We want libpam-modules in karmic, and smart 1.2 | 96 | # We want libpam-modules in karmic, and smart 1.2 |
163 | 96 | echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3), python-smartpm (>= 1.2-4)" >> $(landscape_common_substvars) | 97 | echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3), python-smartpm (>= 1.2-4), python-dbus" >> $(landscape_common_substvars) |
164 | 97 | echo "extra:Depends=python-pycurl" >> $(landscape_client_substvars) | 98 | echo "extra:Depends=python-pycurl, hal" >> $(landscape_client_substvars) |
165 | 98 | endif | 99 | endif |
166 | 99 | ifeq (,$(filter $(dist_release),dapper hardy karmic lucid maverick)) | 100 | ifeq (,$(filter $(dist_release),dapper hardy karmic lucid maverick)) |
167 | 101 | # Starting natty, no more hal or dbus | ||
168 | 100 | echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3), python-smartpm (>= 1.2-4)" >> $(landscape_common_substvars) | 102 | echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3), python-smartpm (>= 1.2-4)" >> $(landscape_common_substvars) |
169 | 101 | echo "extra:Depends=python-pycurl, gir1.2-gudev-1.0 (>= 165-0ubuntu2)" >> $(landscape_client_substvars) | 103 | echo "extra:Depends=python-pycurl, gir1.2-gudev-1.0 (>= 165-0ubuntu2)" >> $(landscape_client_substvars) |
170 | 104 | echo "extra:Suggests=python-dbus, hal" >> $(landscape_client_substvars) | ||
171 | 102 | endif | 105 | endif |
172 | 103 | ifeq (,$(filter $(dist_release),dapper hardy karmic)) | 106 | ifeq (,$(filter $(dist_release),dapper hardy karmic)) |
173 | 104 | # The python-image-store-proxy package is needed for the eucalyptus plugin | 107 | # The python-image-store-proxy package is needed for the eucalyptus plugin |
174 | 105 | 108 | ||
175 | === modified file 'landscape/broker/exchange.py' | |||
176 | --- landscape/broker/exchange.py 2011-12-01 14:01:29 +0000 | |||
177 | +++ landscape/broker/exchange.py 2012-03-06 09:20:24 +0000 | |||
178 | @@ -422,7 +422,9 @@ | |||
179 | 422 | handler(message) | 422 | handler(message) |
180 | 423 | 423 | ||
181 | 424 | def register_client_accepted_message_type(self, type): | 424 | def register_client_accepted_message_type(self, type): |
183 | 425 | self._client_accepted_types.add(type) | 425 | # stringify the type because it's a dbus.String. It should work |
184 | 426 | # anyway, but this is just for sanity and less confusing logs. | ||
185 | 427 | self._client_accepted_types.add(str(type)) | ||
186 | 426 | 428 | ||
187 | 427 | def get_client_accepted_message_types(self): | 429 | def get_client_accepted_message_types(self): |
188 | 428 | return sorted(self._client_accepted_types) | 430 | return sorted(self._client_accepted_types) |
189 | 429 | 431 | ||
190 | === added file 'landscape/hal.py' | |||
191 | --- landscape/hal.py 1970-01-01 00:00:00 +0000 | |||
192 | +++ landscape/hal.py 2012-03-06 09:20:24 +0000 | |||
193 | @@ -0,0 +1,52 @@ | |||
194 | 1 | import logging | ||
195 | 2 | |||
196 | 3 | from dbus import Interface, SystemBus | ||
197 | 4 | from dbus.exceptions import DBusException | ||
198 | 5 | |||
199 | 6 | |||
200 | 7 | class HALManager(object): | ||
201 | 8 | |||
202 | 9 | def __init__(self, bus=None): | ||
203 | 10 | try: | ||
204 | 11 | self._bus = bus or SystemBus() | ||
205 | 12 | manager = self._bus.get_object("org.freedesktop.Hal", | ||
206 | 13 | "/org/freedesktop/Hal/Manager") | ||
207 | 14 | except DBusException: | ||
208 | 15 | logging.error("Couldn't connect to Hal via DBus") | ||
209 | 16 | self._manager = None | ||
210 | 17 | else: | ||
211 | 18 | self._manager = Interface(manager, "org.freedesktop.Hal.Manager") | ||
212 | 19 | |||
213 | 20 | def get_devices(self): | ||
214 | 21 | """Returns a list of HAL devices. | ||
215 | 22 | |||
216 | 23 | @note: If it wasn't possible to connect to HAL over DBus, then an | ||
217 | 24 | empty list will be returned. This can happen if the HAL or DBus | ||
218 | 25 | services are not running. | ||
219 | 26 | """ | ||
220 | 27 | if not self._manager: | ||
221 | 28 | return [] | ||
222 | 29 | devices = [] | ||
223 | 30 | for udi in self._manager.GetAllDevices(): | ||
224 | 31 | device = self._bus.get_object("org.freedesktop.Hal", udi) | ||
225 | 32 | device = Interface(device, "org.freedesktop.Hal.Device") | ||
226 | 33 | device = HALDevice(device) | ||
227 | 34 | devices.append(device) | ||
228 | 35 | return devices | ||
229 | 36 | |||
230 | 37 | |||
231 | 38 | class HALDevice(object): | ||
232 | 39 | |||
233 | 40 | def __init__(self, device): | ||
234 | 41 | self._children = [] | ||
235 | 42 | self._device = device | ||
236 | 43 | self.properties = device.GetAllProperties() | ||
237 | 44 | self.udi = self.properties["info.udi"] | ||
238 | 45 | self.parent = None | ||
239 | 46 | |||
240 | 47 | def add_child(self, device): | ||
241 | 48 | self._children.append(device) | ||
242 | 49 | device.parent = self | ||
243 | 50 | |||
244 | 51 | def get_children(self): | ||
245 | 52 | return self._children | ||
246 | 0 | 53 | ||
247 | === added file 'landscape/lib/bpickle_dbus.py' | |||
248 | --- landscape/lib/bpickle_dbus.py 1970-01-01 00:00:00 +0000 | |||
249 | +++ landscape/lib/bpickle_dbus.py 2012-03-06 09:20:24 +0000 | |||
250 | @@ -0,0 +1,65 @@ | |||
251 | 1 | """ | ||
252 | 2 | Different versions of the Python DBus bindings return different types | ||
253 | 3 | to represent integers, strings, lists, etc. Older versions return | ||
254 | 4 | builtin Python types: C{int}, C{str}, C{list}, etc. Newer versions | ||
255 | 5 | return DBus-specific wrappers: C{Int16}, C{String}, C{Array}, etc. | ||
256 | 6 | Failures occur when DBus types are used because bpickle doesn't know | ||
257 | 7 | that an C{Int16} is really an C{int} and that an C{Array} is really a | ||
258 | 8 | C{list}. | ||
259 | 9 | |||
260 | 10 | L{install} and L{uninstall} can install and remove extensions that | ||
261 | 11 | make bpickle work with DBus types. | ||
262 | 12 | """ | ||
263 | 13 | |||
264 | 14 | import dbus | ||
265 | 15 | |||
266 | 16 | from landscape.lib import bpickle | ||
267 | 17 | |||
268 | 18 | |||
269 | 19 | def install(): | ||
270 | 20 | """Install bpickle extensions for DBus types.""" | ||
271 | 21 | for type, function in get_dbus_types(): | ||
272 | 22 | bpickle.dumps_table[type] = function | ||
273 | 23 | |||
274 | 24 | |||
275 | 25 | def uninstall(): | ||
276 | 26 | """Uninstall bpickle extensions for DBus types.""" | ||
277 | 27 | for type, function in get_dbus_types(): | ||
278 | 28 | del bpickle.dumps_table[type] | ||
279 | 29 | |||
280 | 30 | |||
281 | 31 | def dumps_utf8string(obj): | ||
282 | 32 | """ | ||
283 | 33 | Convert the specified L{dbus.types.UTF8String} to bpickle's | ||
284 | 34 | representation for C{unicode} data. | ||
285 | 35 | """ | ||
286 | 36 | return "u%s:%s" % (len(obj), obj) | ||
287 | 37 | |||
288 | 38 | |||
289 | 39 | def dumps_double(obj): | ||
290 | 40 | """ | ||
291 | 41 | Convert a dbus.types.Double into a floating point representation. | ||
292 | 42 | """ | ||
293 | 43 | return "f%r;" % float(obj) | ||
294 | 44 | |||
295 | 45 | |||
296 | 46 | def get_dbus_types(): | ||
297 | 47 | """ | ||
298 | 48 | Generator yields C{(type, bpickle_function)} for available DBus | ||
299 | 49 | types. | ||
300 | 50 | """ | ||
301 | 51 | for (type_name, function) in [("Boolean", bpickle.dumps_bool), | ||
302 | 52 | ("Int16", bpickle.dumps_int), | ||
303 | 53 | ("UInt16", bpickle.dumps_int), | ||
304 | 54 | ("Int32", bpickle.dumps_int), | ||
305 | 55 | ("UInt32", bpickle.dumps_int), | ||
306 | 56 | ("Int64", bpickle.dumps_int), | ||
307 | 57 | ("UInt64", bpickle.dumps_int), | ||
308 | 58 | ("Double", dumps_double), | ||
309 | 59 | ("Array", bpickle.dumps_list), | ||
310 | 60 | ("Dictionary", bpickle.dumps_dict), | ||
311 | 61 | ("String", bpickle.dumps_unicode), | ||
312 | 62 | ("UTF8String", dumps_utf8string)]: | ||
313 | 63 | type = getattr(dbus.types, type_name, None) | ||
314 | 64 | if type is not None: | ||
315 | 65 | yield type, function | ||
316 | 0 | 66 | ||
317 | === modified file 'landscape/monitor/config.py' | |||
318 | --- landscape/monitor/config.py 2011-11-30 09:28:10 +0000 | |||
319 | +++ landscape/monitor/config.py 2012-03-06 09:20:24 +0000 | |||
320 | @@ -1,10 +1,11 @@ | |||
321 | 1 | from landscape.deployment import Configuration | 1 | from landscape.deployment import Configuration |
322 | 2 | 2 | ||
323 | 3 | 3 | ||
328 | 4 | ALL_PLUGINS = ["ActiveProcessInfo", "ComputerInfo", "LoadAverage", | 4 | ALL_PLUGINS = ["ActiveProcessInfo", "ComputerInfo", "HardwareInventory", |
329 | 5 | "MemoryInfo", "MountInfo", "ProcessorInfo", "Temperature", | 5 | "LoadAverage", "MemoryInfo", "MountInfo", "ProcessorInfo", |
330 | 6 | "PackageMonitor", "UserMonitor", "RebootRequired", | 6 | "Temperature", "PackageMonitor", "UserMonitor", |
331 | 7 | "AptPreferences", "NetworkActivity", "NetworkDevice"] | 7 | "RebootRequired", "AptPreferences", "NetworkActivity", |
332 | 8 | "NetworkDevice"] | ||
333 | 8 | 9 | ||
334 | 9 | 10 | ||
335 | 10 | class MonitorConfiguration(Configuration): | 11 | class MonitorConfiguration(Configuration): |
336 | 11 | 12 | ||
337 | === added file 'landscape/monitor/hardwareinventory.py' | |||
338 | --- landscape/monitor/hardwareinventory.py 1970-01-01 00:00:00 +0000 | |||
339 | +++ landscape/monitor/hardwareinventory.py 2012-03-06 09:20:24 +0000 | |||
340 | @@ -0,0 +1,114 @@ | |||
341 | 1 | import logging | ||
342 | 2 | |||
343 | 3 | from twisted.internet.defer import succeed | ||
344 | 4 | |||
345 | 5 | from landscape.lib.log import log_failure | ||
346 | 6 | |||
347 | 7 | from landscape.diff import diff | ||
348 | 8 | from landscape.monitor.plugin import MonitorPlugin | ||
349 | 9 | |||
350 | 10 | |||
351 | 11 | class HardwareInventory(MonitorPlugin): | ||
352 | 12 | |||
353 | 13 | persist_name = "hardware-inventory" | ||
354 | 14 | |||
355 | 15 | def __init__(self, hal_manager=None): | ||
356 | 16 | super(HardwareInventory, self).__init__() | ||
357 | 17 | self._persist_sets = [] | ||
358 | 18 | self._persist_removes = [] | ||
359 | 19 | self.enabled = True | ||
360 | 20 | try: | ||
361 | 21 | from landscape.hal import HALManager | ||
362 | 22 | except ImportError: | ||
363 | 23 | self.enabled = False | ||
364 | 24 | else: | ||
365 | 25 | self._hal_manager = hal_manager or HALManager() | ||
366 | 26 | |||
367 | 27 | def register(self, manager): | ||
368 | 28 | if not self.enabled: | ||
369 | 29 | return | ||
370 | 30 | super(HardwareInventory, self).register(manager) | ||
371 | 31 | self.call_on_accepted("hardware-inventory", self.exchange, True) | ||
372 | 32 | |||
373 | 33 | def send_message(self, urgent): | ||
374 | 34 | devices = self.create_message() | ||
375 | 35 | if devices: | ||
376 | 36 | message = {"type": "hardware-inventory", "devices": devices} | ||
377 | 37 | result = self.registry.broker.send_message(message, urgent=urgent) | ||
378 | 38 | result.addCallback(self.persist_data) | ||
379 | 39 | result.addErrback(log_failure) | ||
380 | 40 | logging.info("Queueing a message with hardware-inventory " | ||
381 | 41 | "information.") | ||
382 | 42 | else: | ||
383 | 43 | result = succeed(None) | ||
384 | 44 | return result | ||
385 | 45 | |||
386 | 46 | def exchange(self, urgent=False): | ||
387 | 47 | if not self.enabled: | ||
388 | 48 | return | ||
389 | 49 | return self.registry.broker.call_if_accepted("hardware-inventory", | ||
390 | 50 | self.send_message, urgent) | ||
391 | 51 | |||
392 | 52 | def persist_data(self, message_id): | ||
393 | 53 | for key, udi, value in self._persist_sets: | ||
394 | 54 | self._persist.set((key, udi), value) | ||
395 | 55 | for key in self._persist_removes: | ||
396 | 56 | self._persist.remove(key) | ||
397 | 57 | del self._persist_sets[:] | ||
398 | 58 | del self._persist_removes[:] | ||
399 | 59 | # This forces the registry to write the persistent store to disk | ||
400 | 60 | # This means that the persistent data reflects the state of the | ||
401 | 61 | # messages sent. | ||
402 | 62 | self.registry.flush() | ||
403 | 63 | |||
404 | 64 | def create_message(self): | ||
405 | 65 | # FIXME Using persist to keep track of changes here uses a | ||
406 | 66 | # fair amount of memory. On my machine a rough test seemed to | ||
407 | 67 | # indicate that memory usage grew by 1.3mb, about 12% of the | ||
408 | 68 | # overall process size. Look here to save memory. | ||
409 | 69 | del self._persist_sets[:] | ||
410 | 70 | del self._persist_removes[:] | ||
411 | 71 | devices = [] | ||
412 | 72 | previous_devices = self._persist.get("devices", {}) | ||
413 | 73 | current_devices = set() | ||
414 | 74 | |||
415 | 75 | for device in self._hal_manager.get_devices(): | ||
416 | 76 | previous_properties = previous_devices.get(device.udi) | ||
417 | 77 | if not previous_properties: | ||
418 | 78 | devices.append(("create", device.properties)) | ||
419 | 79 | elif previous_properties != device.properties: | ||
420 | 80 | creates, updates, deletes = diff(previous_properties, | ||
421 | 81 | device.properties) | ||
422 | 82 | devices.append(("update", device.udi, | ||
423 | 83 | creates, updates, deletes)) | ||
424 | 84 | current_devices.add(device.udi) | ||
425 | 85 | self._persist_sets.append( | ||
426 | 86 | ("devices", device.udi, device.properties)) | ||
427 | 87 | |||
428 | 88 | items_with_parents = {} | ||
429 | 89 | deleted_devices = set() | ||
430 | 90 | for udi, value in previous_devices.iteritems(): | ||
431 | 91 | if udi not in current_devices: | ||
432 | 92 | if "info.parent" in value: | ||
433 | 93 | items_with_parents[udi] = value["info.parent"] | ||
434 | 94 | deleted_devices.add(udi) | ||
435 | 95 | |||
436 | 96 | # We remove the deleted devices from our persistent store it's | ||
437 | 97 | # only the information we're sending to the server that we're | ||
438 | 98 | # compressing. | ||
439 | 99 | for udi in deleted_devices: | ||
440 | 100 | self._persist_removes.append(("devices", udi)) | ||
441 | 101 | |||
442 | 102 | # We can now flatten the list of devices we send to the server | ||
443 | 103 | # For each of the items_with_parents, if both the item and it's parent | ||
444 | 104 | # are in the deleted_devices set, then we can remove this item from the | ||
445 | 105 | # set. | ||
446 | 106 | minimal_deleted_devices = deleted_devices.copy() | ||
447 | 107 | for child, parent in items_with_parents.iteritems(): | ||
448 | 108 | if child in deleted_devices and parent in deleted_devices: | ||
449 | 109 | minimal_deleted_devices.remove(child) | ||
450 | 110 | # We now build the deleted devices message | ||
451 | 111 | for udi in minimal_deleted_devices: | ||
452 | 112 | devices.append(("delete", udi)) | ||
453 | 113 | |||
454 | 114 | return devices | ||
455 | 0 | 115 | ||
456 | === modified file 'landscape/monitor/mountinfo.py' | |||
457 | --- landscape/monitor/mountinfo.py 2011-12-01 13:38:58 +0000 | |||
458 | +++ landscape/monitor/mountinfo.py 2012-03-06 09:20:24 +0000 | |||
459 | @@ -15,7 +15,7 @@ | |||
460 | 15 | 15 | ||
461 | 16 | def __init__(self, interval=300, monitor_interval=60 * 60, | 16 | def __init__(self, interval=300, monitor_interval=60 * 60, |
462 | 17 | mounts_file="/proc/mounts", create_time=time.time, | 17 | mounts_file="/proc/mounts", create_time=time.time, |
464 | 18 | statvfs=None, mtab_file="/etc/mtab"): | 18 | statvfs=None, hal_manager=None, mtab_file="/etc/mtab"): |
465 | 19 | self.run_interval = interval | 19 | self.run_interval = interval |
466 | 20 | self._monitor_interval = monitor_interval | 20 | self._monitor_interval = monitor_interval |
467 | 21 | self._create_time = create_time | 21 | self._create_time = create_time |
468 | @@ -29,6 +29,12 @@ | |||
469 | 29 | self._mount_info = [] | 29 | self._mount_info = [] |
470 | 30 | self._mount_info_to_persist = None | 30 | self._mount_info_to_persist = None |
471 | 31 | try: | 31 | try: |
472 | 32 | from landscape.hal import HALManager | ||
473 | 33 | except ImportError: | ||
474 | 34 | self._hal_manager = hal_manager | ||
475 | 35 | else: | ||
476 | 36 | self._hal_manager = hal_manager or HALManager() | ||
477 | 37 | try: | ||
478 | 32 | from gi.repository import GUdev | 38 | from gi.repository import GUdev |
479 | 33 | except ImportError: | 39 | except ImportError: |
480 | 34 | self._gudev_client = None | 40 | self._gudev_client = None |
481 | @@ -116,7 +122,9 @@ | |||
482 | 116 | current_mount_points.add(mount_point) | 122 | current_mount_points.add(mount_point) |
483 | 117 | 123 | ||
484 | 118 | def _get_removable_devices(self): | 124 | def _get_removable_devices(self): |
486 | 119 | if self._gudev_client is not None: | 125 | if self._hal_manager is not None: |
487 | 126 | return self._get_hal_removable_devices() | ||
488 | 127 | elif self._gudev_client is not None: | ||
489 | 120 | return self._get_udev_removable_devices() | 128 | return self._get_udev_removable_devices() |
490 | 121 | else: | 129 | else: |
491 | 122 | return set() | 130 | return set() |
492 | @@ -130,6 +138,61 @@ | |||
493 | 130 | return False | 138 | return False |
494 | 131 | return is_removable() | 139 | return is_removable() |
495 | 132 | 140 | ||
496 | 141 | def _get_hal_removable_devices(self): | ||
497 | 142 | block_devices = {} # {udi: [device, ...]} | ||
498 | 143 | children = {} # {parent_udi: [child_udi, ...]} | ||
499 | 144 | removable = set() | ||
500 | 145 | |||
501 | 146 | # We walk the list of devices building up a dictionary of all removable | ||
502 | 147 | # devices, and a mapping of {UDI => [block devices]} | ||
503 | 148 | # We differentiate between devices that we definitely know are | ||
504 | 149 | # removable and devices that _may_ be removable, depending on their | ||
505 | 150 | # parent device, e.g. /dev/sdb1 isn't flagged as removable, but | ||
506 | 151 | # /dev/sdb may well be removable. | ||
507 | 152 | |||
508 | 153 | # Unfortunately, HAL doesn't guarantee the order of the devices | ||
509 | 154 | # returned from get_devices(), so we may not know that a parent device | ||
510 | 155 | # is removable when we find it's first child. | ||
511 | 156 | devices = self._hal_manager.get_devices() | ||
512 | 157 | for device in devices: | ||
513 | 158 | block_device = device.properties.get("block.device") | ||
514 | 159 | if block_device: | ||
515 | 160 | if device.properties.get("storage.removable"): | ||
516 | 161 | removable.add(device.udi) | ||
517 | 162 | |||
518 | 163 | try: | ||
519 | 164 | block_devices[device.udi].append(block_device) | ||
520 | 165 | except KeyError: | ||
521 | 166 | block_devices[device.udi] = [block_device] | ||
522 | 167 | |||
523 | 168 | parent_udi = device.properties.get("info.parent") | ||
524 | 169 | if parent_udi is not None: | ||
525 | 170 | try: | ||
526 | 171 | children[parent_udi].append(device.udi) | ||
527 | 172 | except KeyError: | ||
528 | 173 | children[parent_udi] = [device.udi] | ||
529 | 174 | |||
530 | 175 | # Propagate the removable flag from each node all the way to | ||
531 | 176 | # its leaf children. | ||
532 | 177 | updated = True | ||
533 | 178 | while updated: | ||
534 | 179 | updated = False | ||
535 | 180 | for parent_udi in children: | ||
536 | 181 | if parent_udi in removable: | ||
537 | 182 | for child_udi in children[parent_udi]: | ||
538 | 183 | if child_udi not in removable: | ||
539 | 184 | removable.add(child_udi) | ||
540 | 185 | updated = True | ||
541 | 186 | |||
542 | 187 | # We've now seen _all_ devices, and have the definitive list of | ||
543 | 188 | # removable UDIs, so we can now find all the removable devices in the | ||
544 | 189 | # system. | ||
545 | 190 | removable_devices = set() | ||
546 | 191 | for udi in removable: | ||
547 | 192 | removable_devices.update(block_devices[udi]) | ||
548 | 193 | |||
549 | 194 | return removable_devices | ||
550 | 195 | |||
551 | 133 | def _get_mount_info(self): | 196 | def _get_mount_info(self): |
552 | 134 | """Generator yields local mount points worth recording data for.""" | 197 | """Generator yields local mount points worth recording data for.""" |
553 | 135 | removable_devices = self._get_removable_devices() | 198 | removable_devices = self._get_removable_devices() |
554 | 136 | 199 | ||
555 | === added file 'landscape/monitor/tests/test_hardwareinventory.py' | |||
556 | --- landscape/monitor/tests/test_hardwareinventory.py 1970-01-01 00:00:00 +0000 | |||
557 | +++ landscape/monitor/tests/test_hardwareinventory.py 2012-03-06 09:20:24 +0000 | |||
558 | @@ -0,0 +1,273 @@ | |||
559 | 1 | from twisted.internet.defer import fail, succeed | ||
560 | 2 | |||
561 | 3 | from landscape.monitor.hardwareinventory import HardwareInventory | ||
562 | 4 | from landscape.tests.test_hal import MockHALManager, MockRealHALDevice | ||
563 | 5 | from landscape.tests.helpers import LandscapeTest, MonitorHelper | ||
564 | 6 | from landscape.tests.mocker import ANY | ||
565 | 7 | from landscape.message_schemas import HARDWARE_INVENTORY | ||
566 | 8 | |||
567 | 9 | |||
568 | 10 | class HardwareInventoryTest(LandscapeTest): | ||
569 | 11 | |||
570 | 12 | helpers = [MonitorHelper] | ||
571 | 13 | |||
572 | 14 | def setUp(self): | ||
573 | 15 | super(HardwareInventoryTest, self).setUp() | ||
574 | 16 | self.mstore.set_accepted_types(["hardware-inventory"]) | ||
575 | 17 | devices = [MockRealHALDevice({u"info.udi": u"wubble", | ||
576 | 18 | u"info.product": u"Wubble"}), | ||
577 | 19 | MockRealHALDevice({u"info.udi": u"ooga", | ||
578 | 20 | u"info.product": u"Ooga"})] | ||
579 | 21 | self.hal_manager = MockHALManager(devices) | ||
580 | 22 | self.plugin = HardwareInventory(hal_manager=self.hal_manager) | ||
581 | 23 | self.monitor.add(self.plugin) | ||
582 | 24 | |||
583 | 25 | def assertSchema(self, devices): | ||
584 | 26 | full_message = {"type": "hardware-inventory", "devices": devices} | ||
585 | 27 | self.assertEqual(HARDWARE_INVENTORY.coerce(full_message), | ||
586 | 28 | full_message) | ||
587 | 29 | |||
588 | 30 | def test_hal_devices(self): | ||
589 | 31 | """ | ||
590 | 32 | The first time the plugin runs it should report information | ||
591 | 33 | about all HAL devices found on the system. Every UDI provided | ||
592 | 34 | by HAL should be present in the devices list as is from HAL. | ||
593 | 35 | """ | ||
594 | 36 | message = self.plugin.create_message() | ||
595 | 37 | actual_udis = [part[1][u"info.udi"] for part in message] | ||
596 | 38 | expected_udis = [device.udi for device | ||
597 | 39 | in self.hal_manager.get_devices()] | ||
598 | 40 | self.assertEqual(set(actual_udis), set(expected_udis)) | ||
599 | 41 | |||
600 | 42 | def test_first_message(self): | ||
601 | 43 | """ | ||
602 | 44 | The first time the plugin runs it should report information | ||
603 | 45 | about all HAL devices found on the system. All new devices | ||
604 | 46 | will be reported with 'create' actions. | ||
605 | 47 | """ | ||
606 | 48 | message = self.plugin.create_message() | ||
607 | 49 | actions = [part[0] for part in message] | ||
608 | 50 | self.assertEqual(set(actions), set(["create"])) | ||
609 | 51 | self.assertSchema(message) | ||
610 | 52 | |||
611 | 53 | def test_no_changes(self): | ||
612 | 54 | """ | ||
613 | 55 | Messages should not be created if hardware information is | ||
614 | 56 | unchanged since the last server exchange. | ||
615 | 57 | """ | ||
616 | 58 | self.plugin.exchange() | ||
617 | 59 | self.assertNotEquals(len(self.mstore.get_pending_messages()), 0) | ||
618 | 60 | |||
619 | 61 | messages = self.mstore.get_pending_messages() | ||
620 | 62 | self.plugin.exchange() | ||
621 | 63 | self.assertEqual(self.mstore.get_pending_messages(), messages) | ||
622 | 64 | |||
623 | 65 | def test_update(self): | ||
624 | 66 | """ | ||
625 | 67 | If a change is detected for a device that was previously | ||
626 | 68 | reported to the server, the changed device should be reported | ||
627 | 69 | with an 'update' action. Property changes are reported at a | ||
628 | 70 | key/value pair level. | ||
629 | 71 | """ | ||
630 | 72 | self.hal_manager.devices = [ | ||
631 | 73 | MockRealHALDevice({u"info.udi": u"wubble", | ||
632 | 74 | u"info.product": u"Wubble"})] | ||
633 | 75 | registry_mocker = self.mocker.replace(self.plugin.registry) | ||
634 | 76 | registry_mocker.flush() | ||
635 | 77 | self.mocker.count(2) | ||
636 | 78 | self.mocker.result(None) | ||
637 | 79 | self.mocker.replay() | ||
638 | 80 | message = self.plugin.create_message() | ||
639 | 81 | self.plugin.persist_data(None) | ||
640 | 82 | self.assertEqual(message, [("create", {u"info.udi": u"wubble", | ||
641 | 83 | u"info.product": u"Wubble"})]) | ||
642 | 84 | |||
643 | 85 | self.hal_manager.devices[0] = MockRealHALDevice( | ||
644 | 86 | {u"info.udi": u"wubble", u"info.product": u"Ooga"}) | ||
645 | 87 | message = self.plugin.create_message() | ||
646 | 88 | self.plugin.persist_data(None) | ||
647 | 89 | self.assertEqual(message, [("update", u"wubble", | ||
648 | 90 | {}, {u"info.product": u"Ooga"}, {})]) | ||
649 | 91 | self.assertSchema(message) | ||
650 | 92 | self.assertEqual(self.plugin.create_message(), []) | ||
651 | 93 | |||
652 | 94 | def test_update_list(self): | ||
653 | 95 | """ | ||
654 | 96 | An update should be sent to the server when a strlist device | ||
655 | 97 | property changes. No updates should be sent if a device is | ||
656 | 98 | unchanged. | ||
657 | 99 | """ | ||
658 | 100 | self.hal_manager.devices = [ | ||
659 | 101 | MockRealHALDevice({u"info.udi": u"wubble", | ||
660 | 102 | u"info.product": u"Wubble", | ||
661 | 103 | u"info.capabilities": [u"foo", u"bar"]})] | ||
662 | 104 | |||
663 | 105 | message = self.plugin.create_message() | ||
664 | 106 | self.plugin.persist_data(None) | ||
665 | 107 | self.assertEqual(message, [("create", | ||
666 | 108 | {u"info.udi": u"wubble", | ||
667 | 109 | u"info.product": u"Wubble", | ||
668 | 110 | u"info.capabilities": [u"foo", u"bar"]}), | ||
669 | 111 | ]) | ||
670 | 112 | |||
671 | 113 | self.assertSchema(message) | ||
672 | 114 | |||
673 | 115 | self.hal_manager.devices[0] = MockRealHALDevice( | ||
674 | 116 | {u"info.udi": u"wubble", u"info.product": u"Wubble", | ||
675 | 117 | u"info.capabilities": [u"foo"]}) | ||
676 | 118 | message = self.plugin.create_message() | ||
677 | 119 | self.plugin.persist_data(None) | ||
678 | 120 | self.assertEqual(message, [("update", u"wubble", | ||
679 | 121 | {}, {u"info.capabilities": [u"foo"]}, {}), | ||
680 | 122 | ]) | ||
681 | 123 | self.assertSchema(message) | ||
682 | 124 | |||
683 | 125 | self.assertEqual(self.plugin.create_message(), []) | ||
684 | 126 | |||
685 | 127 | def test_update_complex(self): | ||
686 | 128 | """ | ||
687 | 129 | The 'update' action reports property create, update and | ||
688 | 130 | delete changes. | ||
689 | 131 | """ | ||
690 | 132 | self.hal_manager.devices = [ | ||
691 | 133 | MockRealHALDevice({u"info.udi": u"wubble", | ||
692 | 134 | u"info.product": u"Wubble", | ||
693 | 135 | u"linux.acpi_type": 11})] | ||
694 | 136 | |||
695 | 137 | message = self.plugin.create_message() | ||
696 | 138 | self.plugin.persist_data(None) | ||
697 | 139 | self.assertEqual(message, [("create", {u"info.udi": u"wubble", | ||
698 | 140 | u"info.product": u"Wubble", | ||
699 | 141 | u"linux.acpi_type": 11})]) | ||
700 | 142 | |||
701 | 143 | self.hal_manager.devices[0] = MockRealHALDevice( | ||
702 | 144 | {u"info.udi": u"wubble", u"info.product": u"Ooga", | ||
703 | 145 | u"info.category": u"unittest"}) | ||
704 | 146 | message = self.plugin.create_message() | ||
705 | 147 | self.plugin.persist_data(None) | ||
706 | 148 | self.assertEqual(message, [("update", u"wubble", | ||
707 | 149 | {u"info.category": u"unittest"}, | ||
708 | 150 | {u"info.product": u"Ooga"}, | ||
709 | 151 | {u"linux.acpi_type": 11})]) | ||
710 | 152 | self.assertSchema(message) | ||
711 | 153 | |||
712 | 154 | self.assertEqual(self.plugin.create_message(), []) | ||
713 | 155 | |||
714 | 156 | def test_delete(self): | ||
715 | 157 | """ | ||
716 | 158 | If a device that was previously reported is no longer present | ||
717 | 159 | in a system a device entry should be created with a 'delete' | ||
718 | 160 | action. | ||
719 | 161 | """ | ||
720 | 162 | self.hal_manager.devices = [ | ||
721 | 163 | MockRealHALDevice({u"info.udi": u"wubble", | ||
722 | 164 | u"info.product": u"Wubble"}), | ||
723 | 165 | MockRealHALDevice({u"info.udi": u"ooga", | ||
724 | 166 | u"info.product": u"Ooga"})] | ||
725 | 167 | |||
726 | 168 | message = self.plugin.create_message() | ||
727 | 169 | self.plugin.persist_data(None) | ||
728 | 170 | self.assertEqual(message, [("create", {u"info.udi": u"wubble", | ||
729 | 171 | u"info.product": u"Wubble"}), | ||
730 | 172 | ("create", {u"info.udi": u"ooga", | ||
731 | 173 | u"info.product": u"Ooga"})]) | ||
732 | 174 | self.assertSchema(message) | ||
733 | 175 | |||
734 | 176 | self.hal_manager.devices.pop(1) | ||
735 | 177 | message = self.plugin.create_message() | ||
736 | 178 | self.plugin.persist_data(None) | ||
737 | 179 | self.assertEqual(message, [("delete", u"ooga")]) | ||
738 | 180 | self.assertSchema(message) | ||
739 | 181 | self.assertEqual(self.plugin.create_message(), []) | ||
740 | 182 | |||
741 | 183 | def test_minimal_delete(self): | ||
742 | 184 | self.hal_manager.devices = [ | ||
743 | 185 | MockRealHALDevice({u"info.udi": u"wubble", | ||
744 | 186 | u"block.device": u"/dev/scd", | ||
745 | 187 | u"storage.removable": True}), | ||
746 | 188 | MockRealHALDevice({u"info.udi": u"wubble0", | ||
747 | 189 | u"block.device": u"/dev/scd0", | ||
748 | 190 | u"info.parent": u"wubble"}), | ||
749 | 191 | MockRealHALDevice({u"info.udi": u"wubble1", | ||
750 | 192 | u"block.device": u"/dev/scd1", | ||
751 | 193 | u"info.parent": u"wubble"}), | ||
752 | 194 | MockRealHALDevice({u"info.udi": u"wubble2", | ||
753 | 195 | u"block.device": u"/dev/scd1", | ||
754 | 196 | u"info.parent": u"wubble0"}), | ||
755 | 197 | MockRealHALDevice({u"info.udi": u"wubble3", | ||
756 | 198 | u"block.device": u"/dev/scd1", | ||
757 | 199 | u"info.parent": u"wubble2"})] | ||
758 | 200 | |||
759 | 201 | message = self.plugin.create_message() | ||
760 | 202 | self.plugin.persist_data(None) | ||
761 | 203 | |||
762 | 204 | del self.hal_manager.devices[:] | ||
763 | 205 | |||
764 | 206 | message = self.plugin.create_message() | ||
765 | 207 | self.plugin.persist_data(None) | ||
766 | 208 | |||
767 | 209 | self.assertEqual(message, [("delete", u"wubble")]) | ||
768 | 210 | self.assertEqual(self.plugin.create_message(), []) | ||
769 | 211 | |||
770 | 212 | def test_resynchronize(self): | ||
771 | 213 | """ | ||
772 | 214 | If a 'resynchronize' reactor event is fired, the plugin should | ||
773 | 215 | send a message that contains all data as if the server has | ||
774 | 216 | none. | ||
775 | 217 | """ | ||
776 | 218 | self.plugin.exchange() | ||
777 | 219 | self.reactor.fire("resynchronize") | ||
778 | 220 | self.plugin.exchange() | ||
779 | 221 | |||
780 | 222 | messages = self.mstore.get_pending_messages() | ||
781 | 223 | self.assertEqual(len(messages), 2) | ||
782 | 224 | self.assertEqual(messages[0]["devices"], messages[1]["devices"]) | ||
783 | 225 | |||
784 | 226 | def test_call_on_accepted(self): | ||
785 | 227 | remote_broker_mock = self.mocker.replace(self.remote) | ||
786 | 228 | remote_broker_mock.send_message(ANY, urgent=True) | ||
787 | 229 | self.mocker.result(succeed(None)) | ||
788 | 230 | self.mocker.replay() | ||
789 | 231 | |||
790 | 232 | self.reactor.fire(("message-type-acceptance-changed", | ||
791 | 233 | "hardware-inventory"), | ||
792 | 234 | True) | ||
793 | 235 | |||
794 | 236 | def test_no_message_if_not_accepted(self): | ||
795 | 237 | """ | ||
796 | 238 | Don't add any messages at all if the broker isn't currently | ||
797 | 239 | accepting their type. | ||
798 | 240 | """ | ||
799 | 241 | self.mstore.set_accepted_types([]) | ||
800 | 242 | self.reactor.advance(self.monitor.step_size * 2) | ||
801 | 243 | self.monitor.exchange() | ||
802 | 244 | |||
803 | 245 | self.mstore.set_accepted_types(["hardware-inventory"]) | ||
804 | 246 | self.assertMessages(list(self.mstore.get_pending_messages()), []) | ||
805 | 247 | |||
806 | 248 | def test_do_not_persist_changes_when_send_message_fails(self): | ||
807 | 249 | """ | ||
808 | 250 | When the plugin is run it persists data that it uses on | ||
809 | 251 | subsequent checks to calculate the delta to send. It should | ||
810 | 252 | only persist data when the broker confirms that the message | ||
811 | 253 | sent by the plugin has been sent. | ||
812 | 254 | """ | ||
813 | 255 | |||
814 | 256 | class MyException(Exception): | ||
815 | 257 | pass | ||
816 | 258 | |||
817 | 259 | self.log_helper.ignore_errors(MyException) | ||
818 | 260 | |||
819 | 261 | broker_mock = self.mocker.replace(self.monitor.broker) | ||
820 | 262 | broker_mock.send_message(ANY, urgent=ANY) | ||
821 | 263 | self.mocker.result(fail(MyException())) | ||
822 | 264 | self.mocker.replay() | ||
823 | 265 | |||
824 | 266 | message = self.plugin.create_message() | ||
825 | 267 | |||
826 | 268 | def assert_message(message_id): | ||
827 | 269 | self.assertEqual(message, self.plugin.create_message()) | ||
828 | 270 | |||
829 | 271 | result = self.plugin.exchange() | ||
830 | 272 | result.addCallback(assert_message) | ||
831 | 273 | return result | ||
832 | 0 | 274 | ||
833 | === modified file 'landscape/monitor/tests/test_mountinfo.py' | |||
834 | --- landscape/monitor/tests/test_mountinfo.py 2011-12-01 13:38:58 +0000 | |||
835 | +++ landscape/monitor/tests/test_mountinfo.py 2012-03-06 09:20:24 +0000 | |||
836 | @@ -3,6 +3,7 @@ | |||
837 | 3 | from twisted.internet.defer import succeed | 3 | from twisted.internet.defer import succeed |
838 | 4 | 4 | ||
839 | 5 | from landscape.monitor.mountinfo import MountInfo | 5 | from landscape.monitor.mountinfo import MountInfo |
840 | 6 | from landscape.tests.test_hal import MockHALManager, MockRealHALDevice | ||
841 | 6 | from landscape.tests.helpers import LandscapeTest, mock_counter, MonitorHelper | 7 | from landscape.tests.helpers import LandscapeTest, mock_counter, MonitorHelper |
842 | 7 | from landscape.tests.mocker import ANY | 8 | from landscape.tests.mocker import ANY |
843 | 8 | 9 | ||
844 | @@ -21,6 +22,8 @@ | |||
845 | 21 | self.log_helper.ignore_errors("Typelib file for namespace") | 22 | self.log_helper.ignore_errors("Typelib file for namespace") |
846 | 22 | 23 | ||
847 | 23 | def get_mount_info(self, *args, **kwargs): | 24 | def get_mount_info(self, *args, **kwargs): |
848 | 25 | hal_devices = kwargs.pop("hal_devices", []) | ||
849 | 26 | kwargs["hal_manager"] = MockHALManager(hal_devices) | ||
850 | 24 | if "statvfs" not in kwargs: | 27 | if "statvfs" not in kwargs: |
851 | 25 | kwargs["statvfs"] = lambda path: (0,) * 10 | 28 | kwargs["statvfs"] = lambda path: (0,) * 10 |
852 | 26 | return MountInfo(*args, **kwargs) | 29 | return MountInfo(*args, **kwargs) |
853 | @@ -320,8 +323,51 @@ | |||
854 | 320 | message = plugin.create_mount_info_message() | 323 | message = plugin.create_mount_info_message() |
855 | 321 | self.assertEqual(message, None) | 324 | self.assertEqual(message, None) |
856 | 322 | 325 | ||
857 | 326 | def test_ignore_removable_partitions(self): | ||
858 | 327 | """ | ||
859 | 328 | Partitions on removable devices don't directly report | ||
860 | 329 | storage.removable : True, but they do point to their parent and the | ||
861 | 330 | parent will be marked removable if appropriate. | ||
862 | 331 | """ | ||
863 | 332 | devices = [MockRealHALDevice({"info.udi": "wubble", | ||
864 | 333 | "block.device": "/dev/scd", | ||
865 | 334 | "storage.removable": True}), | ||
866 | 335 | MockRealHALDevice({"info.udi": "wubble0", | ||
867 | 336 | "block.device": "/dev/scd0", | ||
868 | 337 | "info.parent": "wubble"})] | ||
869 | 338 | |||
870 | 339 | filename = self.makeFile("""\ | ||
871 | 340 | /dev/scd0 /media/Xerox_M750 iso9660 ro,nosuid,nodev,uid=1000,utf8 0 0 | ||
872 | 341 | """) | ||
873 | 342 | plugin = self.get_mount_info(mounts_file=filename, hal_devices=devices, | ||
874 | 343 | mtab_file=filename) | ||
875 | 344 | self.monitor.add(plugin) | ||
876 | 345 | plugin.run() | ||
877 | 346 | |||
878 | 347 | message = plugin.create_mount_info_message() | ||
879 | 348 | self.assertEqual(message, None) | ||
880 | 349 | |||
881 | 323 | def test_ignore_removable_devices(self): | 350 | def test_ignore_removable_devices(self): |
882 | 324 | """ | 351 | """ |
883 | 352 | The mount info plugin should only report data about | ||
884 | 353 | non-removable devices. | ||
885 | 354 | """ | ||
886 | 355 | devices = [MockRealHALDevice({"info.udi": "wubble", | ||
887 | 356 | "block.device": "/dev/scd0", | ||
888 | 357 | "storage.removable": True})] | ||
889 | 358 | filename = self.makeFile("""\ | ||
890 | 359 | /dev/scd0 /media/Xerox_M750 iso9660 ro,nosuid,nodev,uid=1000,utf8 0 0 | ||
891 | 360 | """) | ||
892 | 361 | plugin = self.get_mount_info(mounts_file=filename, hal_devices=devices, | ||
893 | 362 | mtab_file=filename) | ||
894 | 363 | self.monitor.add(plugin) | ||
895 | 364 | plugin.run() | ||
896 | 365 | |||
897 | 366 | message = plugin.create_mount_info_message() | ||
898 | 367 | self.assertEqual(message, None) | ||
899 | 368 | |||
900 | 369 | def test_ignore_removable_devices_gudev(self): | ||
901 | 370 | """ | ||
902 | 325 | The mount info plugin uses gudev to retrieve removable information | 371 | The mount info plugin uses gudev to retrieve removable information |
903 | 326 | about devices. | 372 | about devices. |
904 | 327 | """ | 373 | """ |
905 | @@ -330,6 +376,7 @@ | |||
906 | 330 | """) | 376 | """) |
907 | 331 | plugin = self.get_mount_info(mounts_file=filename, | 377 | plugin = self.get_mount_info(mounts_file=filename, |
908 | 332 | mtab_file=filename) | 378 | mtab_file=filename) |
909 | 379 | plugin._hal_manager = None | ||
910 | 333 | 380 | ||
911 | 334 | class MockDevice(object): | 381 | class MockDevice(object): |
912 | 335 | def get_sysfs_attr_as_boolean(self, attr): | 382 | def get_sysfs_attr_as_boolean(self, attr): |
913 | @@ -348,6 +395,35 @@ | |||
914 | 348 | message = plugin.create_mount_info_message() | 395 | message = plugin.create_mount_info_message() |
915 | 349 | self.assertEqual(message, None) | 396 | self.assertEqual(message, None) |
916 | 350 | 397 | ||
917 | 398 | def test_ignore_multiparented_removable_devices(self): | ||
918 | 399 | """ | ||
919 | 400 | Some removable devices might be the grand-children of a device that is | ||
920 | 401 | marked as "storage.removable". | ||
921 | 402 | """ | ||
922 | 403 | devices = [MockRealHALDevice({"info.udi": "wubble", | ||
923 | 404 | "block.device": "/dev/scd", | ||
924 | 405 | "storage.removable": True}), | ||
925 | 406 | MockRealHALDevice({"info.udi": "wubble0", | ||
926 | 407 | "block.device": "/dev/scd0", | ||
927 | 408 | "info.parent": "wubble"}), | ||
928 | 409 | MockRealHALDevice({"info.udi": "wubble0a", | ||
929 | 410 | "block.device": "/dev/scd0a", | ||
930 | 411 | "info.parent": "wubble0"}), | ||
931 | 412 | MockRealHALDevice({"info.udi": "wubble0b", | ||
932 | 413 | "block.device": "/dev/scd0b", | ||
933 | 414 | "info.parent": "wubble0"})] | ||
934 | 415 | |||
935 | 416 | filename = self.makeFile("""\ | ||
936 | 417 | /dev/scd0a /media/Xerox_M750 iso9660 ro,nosuid,nodev,uid=1000,utf8 0 0 | ||
937 | 418 | """) | ||
938 | 419 | plugin = self.get_mount_info(mounts_file=filename, hal_devices=devices, | ||
939 | 420 | mtab_file=filename) | ||
940 | 421 | self.monitor.add(plugin) | ||
941 | 422 | plugin.run() | ||
942 | 423 | |||
943 | 424 | message = plugin.create_mount_info_message() | ||
944 | 425 | self.assertEqual(message, None) | ||
945 | 426 | |||
946 | 351 | def test_sample_free_space(self): | 427 | def test_sample_free_space(self): |
947 | 352 | """Test collecting information about free space.""" | 428 | """Test collecting information about free space.""" |
948 | 353 | def statvfs(path, multiplier=mock_counter(1).next): | 429 | def statvfs(path, multiplier=mock_counter(1).next): |
949 | 354 | 430 | ||
950 | === modified file 'landscape/monitor/tests/test_service.py' | |||
951 | --- landscape/monitor/tests/test_service.py 2011-12-02 08:35:25 +0000 | |||
952 | +++ landscape/monitor/tests/test_service.py 2012-03-06 09:20:24 +0000 | |||
953 | @@ -1,3 +1,4 @@ | |||
954 | 1 | from landscape.tests.mocker import ANY | ||
955 | 1 | from landscape.tests.helpers import LandscapeTest, FakeBrokerServiceHelper | 2 | from landscape.tests.helpers import LandscapeTest, FakeBrokerServiceHelper |
956 | 2 | from landscape.reactor import FakeReactor | 3 | from landscape.reactor import FakeReactor |
957 | 3 | from landscape.monitor.config import MonitorConfiguration, ALL_PLUGINS | 4 | from landscape.monitor.config import MonitorConfiguration, ALL_PLUGINS |
958 | @@ -45,10 +46,15 @@ | |||
959 | 45 | starts the plugins and register the monitor as broker client. It also | 46 | starts the plugins and register the monitor as broker client. It also |
960 | 46 | start listening on its own socket for incoming connections. | 47 | start listening on its own socket for incoming connections. |
961 | 47 | """ | 48 | """ |
962 | 49 | # FIXME: don't actually run the real register method, because at the | ||
963 | 50 | # moment the UserMonitor plugin still depends on DBus. We can probably | ||
964 | 51 | # drop this mocking once the AMP migration is completed. | ||
965 | 52 | for plugin in self.service.plugins: | ||
966 | 53 | plugin.register = self.mocker.mock() | ||
967 | 54 | plugin.register(ANY) | ||
968 | 55 | self.mocker.replay() | ||
969 | 56 | |||
970 | 48 | def stop_service(ignored): | 57 | def stop_service(ignored): |
971 | 49 | for plugin in self.service.plugins: | ||
972 | 50 | if getattr(plugin, "stop", None) is not None: | ||
973 | 51 | plugin.stop() | ||
974 | 52 | [connector] = self.broker_service.broker.get_connectors() | 58 | [connector] = self.broker_service.broker.get_connectors() |
975 | 53 | connector.disconnect() | 59 | connector.disconnect() |
976 | 54 | self.service.stopService() | 60 | self.service.stopService() |
977 | 55 | 61 | ||
978 | === modified file 'landscape/package/releaseupgrader.py' | |||
979 | --- landscape/package/releaseupgrader.py 2011-12-01 14:01:29 +0000 | |||
980 | +++ landscape/package/releaseupgrader.py 2012-03-06 09:20:24 +0000 | |||
981 | @@ -188,11 +188,32 @@ | |||
982 | 188 | config.add_section("NonInteractive") | 188 | config.add_section("NonInteractive") |
983 | 189 | config.set("NonInteractive", "ForceOverwrite", "no") | 189 | config.set("NonInteractive", "ForceOverwrite", "no") |
984 | 190 | 190 | ||
985 | 191 | # Workaround for Bug #174148, which prevents dbus from restarting | ||
986 | 192 | # after a dapper->hardy upgrade | ||
987 | 193 | if not config.has_section("Distro"): | ||
988 | 194 | config.add_section("Distro") | ||
989 | 195 | if not config.has_option("Distro", "PostInstallScripts"): | ||
990 | 196 | config.set("Distro", "PostInstallScripts", "./dbus.sh") | ||
991 | 197 | else: | ||
992 | 198 | scripts = config.get("Distro", "PostInstallScripts") | ||
993 | 199 | scripts += ", ./dbus.sh" | ||
994 | 200 | config.set("Distro", "PostInstallScripts", scripts) | ||
995 | 201 | |||
996 | 191 | # Write config changes to disk | 202 | # Write config changes to disk |
997 | 192 | fd = open(config_filename, "w") | 203 | fd = open(config_filename, "w") |
998 | 193 | config.write(fd) | 204 | config.write(fd) |
999 | 194 | fd.close() | 205 | fd.close() |
1000 | 195 | 206 | ||
1001 | 207 | # Generate the post-install script that starts DBus | ||
1002 | 208 | dbus_sh_filename = os.path.join(upgrade_tool_directory, | ||
1003 | 209 | "dbus.sh") | ||
1004 | 210 | fd = open(dbus_sh_filename, "w") | ||
1005 | 211 | fd.write("#!/bin/sh\n" | ||
1006 | 212 | "/etc/init.d/dbus start\n" | ||
1007 | 213 | "sleep 10\n") | ||
1008 | 214 | fd.close() | ||
1009 | 215 | os.chmod(dbus_sh_filename, 0755) | ||
1010 | 216 | |||
1011 | 196 | # On some releases the upgrade-tool doesn't support the allow third | 217 | # On some releases the upgrade-tool doesn't support the allow third |
1012 | 197 | # party environment variable, so this trick is needed to make it | 218 | # party environment variable, so this trick is needed to make it |
1013 | 198 | # possible to upgrade against testing client packages from the | 219 | # possible to upgrade against testing client packages from the |
1014 | 199 | 220 | ||
1015 | === modified file 'landscape/package/tests/test_releaseupgrader.py' | |||
1016 | --- landscape/package/tests/test_releaseupgrader.py 2011-12-01 14:01:29 +0000 | |||
1017 | +++ landscape/package/tests/test_releaseupgrader.py 2012-03-06 09:20:24 +0000 | |||
1018 | @@ -245,6 +245,60 @@ | |||
1019 | 245 | result.addCallback(check_result) | 245 | result.addCallback(check_result) |
1020 | 246 | return result | 246 | return result |
1021 | 247 | 247 | ||
1022 | 248 | def test_tweak_sets_dbus_start_script(self): | ||
1023 | 249 | """ | ||
1024 | 250 | The L{ReleaseUpgrader.tweak} method adds to the upgrade-tool | ||
1025 | 251 | configuration a little script that starts dbus after the upgrade. | ||
1026 | 252 | """ | ||
1027 | 253 | config_filename = os.path.join(self.config.upgrade_tool_directory, | ||
1028 | 254 | "DistUpgrade.cfg.dapper") | ||
1029 | 255 | self.makeFile(path=config_filename, | ||
1030 | 256 | content="[Distro]\n" | ||
1031 | 257 | "PostInstallScripts=/foo.sh\n") | ||
1032 | 258 | |||
1033 | 259 | def check_result(ignored): | ||
1034 | 260 | config = ConfigParser.ConfigParser() | ||
1035 | 261 | config.read(config_filename) | ||
1036 | 262 | self.assertEqual(config.get("Distro", "PostInstallScripts"), | ||
1037 | 263 | "/foo.sh, ./dbus.sh") | ||
1038 | 264 | dbus_sh = os.path.join(self.config.upgrade_tool_directory, | ||
1039 | 265 | "dbus.sh") | ||
1040 | 266 | self.assertFileContent(dbus_sh, | ||
1041 | 267 | "#!/bin/sh\n" | ||
1042 | 268 | "/etc/init.d/dbus start\n" | ||
1043 | 269 | "sleep 10\n") | ||
1044 | 270 | |||
1045 | 271 | result = self.upgrader.tweak("dapper") | ||
1046 | 272 | result.addCallback(check_result) | ||
1047 | 273 | return result | ||
1048 | 274 | |||
1049 | 275 | def test_tweak_sets_dbus_start_script_with_no_post_install_scripts(self): | ||
1050 | 276 | """ | ||
1051 | 277 | The L{ReleaseUpgrader.tweak} method adds to the upgrade-tool | ||
1052 | 278 | configuration a little script that starts dbus after the upgrade. This | ||
1053 | 279 | works even when the config file doesn't have a PostInstallScripts entry | ||
1054 | 280 | yet. | ||
1055 | 281 | """ | ||
1056 | 282 | config_filename = os.path.join(self.config.upgrade_tool_directory, | ||
1057 | 283 | "DistUpgrade.cfg.dapper") | ||
1058 | 284 | self.makeFile(path=config_filename, content="") | ||
1059 | 285 | |||
1060 | 286 | def check_result(ignored): | ||
1061 | 287 | config = ConfigParser.ConfigParser() | ||
1062 | 288 | config.read(config_filename) | ||
1063 | 289 | self.assertEqual(config.get("Distro", "PostInstallScripts"), | ||
1064 | 290 | "./dbus.sh") | ||
1065 | 291 | dbus_sh = os.path.join(self.config.upgrade_tool_directory, | ||
1066 | 292 | "dbus.sh") | ||
1067 | 293 | self.assertFileContent(dbus_sh, | ||
1068 | 294 | "#!/bin/sh\n" | ||
1069 | 295 | "/etc/init.d/dbus start\n" | ||
1070 | 296 | "sleep 10\n") | ||
1071 | 297 | |||
1072 | 298 | result = self.upgrader.tweak("dapper") | ||
1073 | 299 | result.addCallback(check_result) | ||
1074 | 300 | return result | ||
1075 | 301 | |||
1076 | 248 | def test_default_logs_directory(self): | 302 | def test_default_logs_directory(self): |
1077 | 249 | """ | 303 | """ |
1078 | 250 | The default directory for the upgrade-tool logs is the system one. | 304 | The default directory for the upgrade-tool logs is the system one. |
1079 | 251 | 305 | ||
1080 | === modified file 'landscape/reactor.py' | |||
1081 | --- landscape/reactor.py 2012-02-03 18:10:57 +0000 | |||
1082 | +++ landscape/reactor.py 2012-03-06 09:20:24 +0000 | |||
1083 | @@ -16,6 +16,10 @@ | |||
1084 | 16 | """Raised when an invalid ID is used with reactor.cancel_call().""" | 16 | """Raised when an invalid ID is used with reactor.cancel_call().""" |
1085 | 17 | 17 | ||
1086 | 18 | 18 | ||
1087 | 19 | class CallHookError(Exception): | ||
1088 | 20 | """Raised when hooking on a reactor incorrectly.""" | ||
1089 | 21 | |||
1090 | 22 | |||
1091 | 19 | class EventID(object): | 23 | class EventID(object): |
1092 | 20 | """Unique identifier for an event handler. | 24 | """Unique identifier for an event handler. |
1093 | 21 | 25 | ||
1094 | @@ -155,6 +159,13 @@ | |||
1095 | 155 | except Exception, e: | 159 | except Exception, e: |
1096 | 156 | logging.exception(e) | 160 | logging.exception(e) |
1097 | 157 | 161 | ||
1098 | 162 | def _hook_threaded_callbacks(self): | ||
1099 | 163 | id = self.call_every(0.5, self._run_threaded_callbacks) | ||
1100 | 164 | self._run_threaded_callbacks_id = id | ||
1101 | 165 | |||
1102 | 166 | def _unhook_threaded_callbacks(self): | ||
1103 | 167 | self.cancel_call(self._run_threaded_callbacks_id) | ||
1104 | 168 | |||
1105 | 158 | 169 | ||
1106 | 159 | class UnixReactorMixin(object): | 170 | class UnixReactorMixin(object): |
1107 | 160 | 171 | ||
1108 | 161 | 172 | ||
1109 | === modified file 'landscape/service.py' | |||
1110 | --- landscape/service.py 2012-02-03 10:06:28 +0000 | |||
1111 | +++ landscape/service.py 2012-03-06 09:20:24 +0000 | |||
1112 | @@ -29,6 +29,12 @@ | |||
1113 | 29 | 29 | ||
1114 | 30 | def __init__(self, config): | 30 | def __init__(self, config): |
1115 | 31 | self.config = config | 31 | self.config = config |
1116 | 32 | try: | ||
1117 | 33 | from landscape.lib import bpickle_dbus | ||
1118 | 34 | except ImportError: | ||
1119 | 35 | pass | ||
1120 | 36 | else: | ||
1121 | 37 | bpickle_dbus.install() | ||
1122 | 32 | self.reactor = self.reactor_factory() | 38 | self.reactor = self.reactor_factory() |
1123 | 33 | if self.persist_filename: | 39 | if self.persist_filename: |
1124 | 34 | self.persist = get_versioned_persist(self) | 40 | self.persist = get_versioned_persist(self) |
1125 | 35 | 41 | ||
1126 | === modified file 'landscape/tests/test_configuration.py' | |||
1127 | --- landscape/tests/test_configuration.py 2012-02-29 22:13:01 +0000 | |||
1128 | +++ landscape/tests/test_configuration.py 2012-03-06 09:20:24 +0000 | |||
1129 | @@ -1883,7 +1883,7 @@ | |||
1130 | 1883 | 1883 | ||
1131 | 1884 | def test_register_bus_connection_failure_ok_no_register(self): | 1884 | def test_register_bus_connection_failure_ok_no_register(self): |
1132 | 1885 | """ | 1885 | """ |
1134 | 1886 | Exit code 0 will be returned if we can't contact Landscape and | 1886 | Exit code 0 will be returned if we can't contact Landscape via DBus and |
1135 | 1887 | --ok-no-register was passed. | 1887 | --ok-no-register was passed. |
1136 | 1888 | """ | 1888 | """ |
1137 | 1889 | print_text_mock = self.mocker.replace(print_text) | 1889 | print_text_mock = self.mocker.replace(print_text) |
1138 | 1890 | 1890 | ||
1139 | === added file 'landscape/tests/test_hal.py' | |||
1140 | --- landscape/tests/test_hal.py 1970-01-01 00:00:00 +0000 | |||
1141 | +++ landscape/tests/test_hal.py 2012-03-06 09:20:24 +0000 | |||
1142 | @@ -0,0 +1,87 @@ | |||
1143 | 1 | from dbus import SystemBus, Interface | ||
1144 | 2 | from dbus.exceptions import DBusException | ||
1145 | 3 | |||
1146 | 4 | from landscape.hal import HALDevice, HALManager | ||
1147 | 5 | from landscape.tests.helpers import LandscapeTest | ||
1148 | 6 | |||
1149 | 7 | |||
1150 | 8 | class HALManagerTest(LandscapeTest): | ||
1151 | 9 | |||
1152 | 10 | def setUp(self): | ||
1153 | 11 | super(HALManagerTest, self).setUp() | ||
1154 | 12 | self.bus = SystemBus() | ||
1155 | 13 | |||
1156 | 14 | def test_get_devices(self): | ||
1157 | 15 | """ | ||
1158 | 16 | A HALManager can return a flat list of devices. All available | ||
1159 | 17 | devices should be included in the returned list. | ||
1160 | 18 | """ | ||
1161 | 19 | devices = HALManager().get_devices() | ||
1162 | 20 | manager = self.bus.get_object("org.freedesktop.Hal", | ||
1163 | 21 | "/org/freedesktop/Hal/Manager") | ||
1164 | 22 | manager = Interface(manager, "org.freedesktop.Hal.Manager") | ||
1165 | 23 | expected_devices = manager.GetAllDevices() | ||
1166 | 24 | actual_devices = [device.udi for device in devices] | ||
1167 | 25 | self.assertEqual(set(expected_devices), set(actual_devices)) | ||
1168 | 26 | |||
1169 | 27 | def test_get_devices_with_dbus_error(self): | ||
1170 | 28 | """ | ||
1171 | 29 | If the L{HALManager} fails connecting to HAL over D-Bus, then the | ||
1172 | 30 | L{HALManager.get_devices} method returns an empty list. | ||
1173 | 31 | """ | ||
1174 | 32 | self.log_helper.ignore_errors("Couldn't connect to Hal via DBus") | ||
1175 | 33 | bus = self.mocker.mock() | ||
1176 | 34 | bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager") | ||
1177 | 35 | self.mocker.throw(DBusException()) | ||
1178 | 36 | self.mocker.replay() | ||
1179 | 37 | devices = HALManager(bus=bus).get_devices() | ||
1180 | 38 | self.assertEqual(devices, []) | ||
1181 | 39 | |||
1182 | 40 | def test_get_devices_with_no_server(self): | ||
1183 | 41 | """ | ||
1184 | 42 | If the L{HALManager} fails connecting to HAL over D-Bus, for example | ||
1185 | 43 | because the DBus server is not running at all, then the | ||
1186 | 44 | L{HALManager.get_devices} method returns an empty list. | ||
1187 | 45 | """ | ||
1188 | 46 | self.log_helper.ignore_errors("Couldn't connect to Hal via DBus") | ||
1189 | 47 | bus_mock = self.mocker.replace("dbus.SystemBus") | ||
1190 | 48 | bus_mock() | ||
1191 | 49 | self.mocker.throw(DBusException()) | ||
1192 | 50 | self.mocker.replay() | ||
1193 | 51 | devices = HALManager().get_devices() | ||
1194 | 52 | self.assertEqual(devices, []) | ||
1195 | 53 | |||
1196 | 54 | |||
1197 | 55 | class MockHALManager(object): | ||
1198 | 56 | |||
1199 | 57 | def __init__(self, devices): | ||
1200 | 58 | self.devices = devices | ||
1201 | 59 | |||
1202 | 60 | def get_devices(self): | ||
1203 | 61 | return [HALDevice(device) for device in self.devices] | ||
1204 | 62 | |||
1205 | 63 | |||
1206 | 64 | class MockRealHALDevice(object): | ||
1207 | 65 | |||
1208 | 66 | def __init__(self, properties): | ||
1209 | 67 | self._properties = properties | ||
1210 | 68 | self.udi = properties.get("info.udi", "fake_udi") | ||
1211 | 69 | |||
1212 | 70 | def GetAllProperties(self): | ||
1213 | 71 | return self._properties | ||
1214 | 72 | |||
1215 | 73 | |||
1216 | 74 | class HALDeviceTest(LandscapeTest): | ||
1217 | 75 | |||
1218 | 76 | def test_init(self): | ||
1219 | 77 | device = HALDevice(MockRealHALDevice({"info.udi": "wubble"})) | ||
1220 | 78 | self.assertEqual(device.properties, {"info.udi": "wubble"}) | ||
1221 | 79 | self.assertEqual(device.udi, "wubble") | ||
1222 | 80 | self.assertEqual(device.parent, None) | ||
1223 | 81 | |||
1224 | 82 | def test_add_child(self): | ||
1225 | 83 | parent = HALDevice(MockRealHALDevice({"info.udi": "wubble"})) | ||
1226 | 84 | child = HALDevice(MockRealHALDevice({"info.udi": "ooga"})) | ||
1227 | 85 | parent.add_child(child) | ||
1228 | 86 | self.assertEqual(parent.get_children(), [child]) | ||
1229 | 87 | self.assertEqual(child.parent, parent) | ||
1230 | 0 | 88 | ||
1231 | === modified file 'landscape/tests/test_service.py' | |||
1232 | --- landscape/tests/test_service.py 2011-12-01 13:38:58 +0000 | |||
1233 | +++ landscape/tests/test_service.py 2012-03-06 09:20:24 +0000 | |||
1234 | @@ -56,6 +56,15 @@ | |||
1235 | 56 | service = TestService(self.config) | 56 | service = TestService(self.config) |
1236 | 57 | self.assertFalse(hasattr(service, "persist")) | 57 | self.assertFalse(hasattr(service, "persist")) |
1237 | 58 | 58 | ||
1238 | 59 | def test_install_bpickle_dbus(self): | ||
1239 | 60 | """ | ||
1240 | 61 | A L{LandscapeService} installs the DBus extensions of bpickle. | ||
1241 | 62 | """ | ||
1242 | 63 | dbus_mock = self.mocker.replace("landscape.lib.bpickle_dbus.install") | ||
1243 | 64 | dbus_mock() | ||
1244 | 65 | self.mocker.replay() | ||
1245 | 66 | TestService(self.config) | ||
1246 | 67 | |||
1247 | 59 | def test_usr1_rotates_logs(self): | 68 | def test_usr1_rotates_logs(self): |
1248 | 60 | """ | 69 | """ |
1249 | 61 | SIGUSR1 should cause logs to be reopened. | 70 | SIGUSR1 should cause logs to be reopened. |
1250 | 62 | 71 | ||
1251 | === modified file 'landscape/tests/test_textmessage.py' | |||
1252 | --- landscape/tests/test_textmessage.py 2011-12-01 14:01:29 +0000 | |||
1253 | +++ landscape/tests/test_textmessage.py 2012-03-06 09:20:24 +0000 | |||
1254 | @@ -13,8 +13,8 @@ | |||
1255 | 13 | 13 | ||
1256 | 14 | def test_send_message(self): | 14 | def test_send_message(self): |
1257 | 15 | """ | 15 | """ |
1260 | 16 | L{send_message} should send a message of type C{text-message} to the | 16 | L{send_message} should send a message of type |
1261 | 17 | landscape messaging service. | 17 | C{text-message} to the landscape dbus messaging service. |
1262 | 18 | """ | 18 | """ |
1263 | 19 | service = self.broker_service | 19 | service = self.broker_service |
1264 | 20 | service.message_store.set_accepted_types(["text-message"]) | 20 | service.message_store.set_accepted_types(["text-message"]) |
1265 | 21 | 21 | ||
1266 | === modified file 'landscape/textmessage.py' | |||
1267 | --- landscape/textmessage.py 2011-12-01 14:01:29 +0000 | |||
1268 | +++ landscape/textmessage.py 2012-03-06 09:20:24 +0000 | |||
1269 | @@ -1,6 +1,7 @@ | |||
1270 | 1 | """ | 1 | """ |
1271 | 2 | Support code for the C{landscape-message} utility, which sends a text | 2 | Support code for the C{landscape-message} utility, which sends a text |
1273 | 3 | message to the Landscape web UI via the landscape-client's messaging service. | 3 | message to the Landscape web UI via the landscape-client's dbus |
1274 | 4 | messaging service (see L{landscape.plugins.dbus_message}). | ||
1275 | 4 | """ | 5 | """ |
1276 | 5 | 6 | ||
1277 | 6 | import sys | 7 | import sys |
1278 | 7 | 8 | ||
1279 | === modified file 'landscape/watchdog.py' | |||
1280 | --- landscape/watchdog.py 2012-02-27 20:49:50 +0000 | |||
1281 | +++ landscape/watchdog.py 2012-03-06 09:20:24 +0000 | |||
1282 | @@ -62,6 +62,8 @@ | |||
1283 | 62 | @cvar program: The name of the executable program that will start this | 62 | @cvar program: The name of the executable program that will start this |
1284 | 63 | daemon. | 63 | daemon. |
1285 | 64 | @cvar username: The name of the user to switch to, by default. | 64 | @cvar username: The name of the user to switch to, by default. |
1286 | 65 | @cvar service: The DBus service name that the program will be expected to | ||
1287 | 66 | listen on. | ||
1288 | 65 | @cvar max_retries: The maximum number of retries before giving up when | 67 | @cvar max_retries: The maximum number of retries before giving up when |
1289 | 66 | trying to connect to the watched daemon. | 68 | trying to connect to the watched daemon. |
1290 | 67 | @cvar factor: The factor by which the delay between subsequent connection | 69 | @cvar factor: The factor by which the delay between subsequent connection |
1291 | @@ -176,7 +178,8 @@ | |||
1292 | 176 | 178 | ||
1293 | 177 | def is_running(self): | 179 | def is_running(self): |
1294 | 178 | # FIXME Error cases may not be handled in the best possible way | 180 | # FIXME Error cases may not be handled in the best possible way |
1296 | 179 | # here. We're basically return False if any error happens. | 181 | # here. We're basically return False if any error happens from the |
1297 | 182 | # dbus ping. | ||
1298 | 180 | return self._connect_and_call("ping") | 183 | return self._connect_and_call("ping") |
1299 | 181 | 184 | ||
1300 | 182 | def wait(self): | 185 | def wait(self): |
1301 | @@ -364,7 +367,7 @@ | |||
1302 | 364 | def start(self): | 367 | def start(self): |
1303 | 365 | """ | 368 | """ |
1304 | 366 | Start all daemons. The broker will be started first, and no other | 369 | Start all daemons. The broker will be started first, and no other |
1306 | 367 | daemons will be started before it is running and responding to | 370 | daemons will be started before it is running and responding to DBUS |
1307 | 368 | messages. | 371 | messages. |
1308 | 369 | 372 | ||
1309 | 370 | @return: A deferred which fires when all services have successfully | 373 | @return: A deferred which fires when all services have successfully |
1310 | 371 | 374 | ||
1311 | === modified file 'man/landscape-client.1' | |||
1312 | --- man/landscape-client.1 2011-12-02 09:52:48 +0000 | |||
1313 | +++ man/landscape-client.1 2012-03-06 09:20:24 +0000 | |||
1314 | @@ -1,5 +1,5 @@ | |||
1315 | 1 | .\"Text automatically generated by txt2man | 1 | .\"Text automatically generated by txt2man |
1317 | 2 | .TH landscape-client 1 "02 December 2011" "" "" | 2 | .TH landscape-client "18 January 2010" "" "" |
1318 | 3 | .SH NAME | 3 | .SH NAME |
1319 | 4 | \fBlandscape-client \fP- Landscape system client | 4 | \fBlandscape-client \fP- Landscape system client |
1320 | 5 | \fB | 5 | \fB |
1321 | @@ -35,6 +35,11 @@ | |||
1322 | 35 | \fIoptions\fP override settings from the file). | 35 | \fIoptions\fP override settings from the file). |
1323 | 36 | .TP | 36 | .TP |
1324 | 37 | .B | 37 | .B |
1325 | 38 | \fB--bus\fP=BUS | ||
1326 | 39 | Which DBUS bus to use. One of 'session' or | ||
1327 | 40 | 'system'. | ||
1328 | 41 | .TP | ||
1329 | 42 | .B | ||
1330 | 38 | \fB-d\fP PATH, \fB--data-path\fP=PATH | 43 | \fB-d\fP PATH, \fB--data-path\fP=PATH |
1331 | 39 | The directory to store data files in. | 44 | The directory to store data files in. |
1332 | 40 | .TP | 45 | .TP |
1333 | 41 | 46 | ||
1334 | === modified file 'man/landscape-client.txt' | |||
1335 | --- man/landscape-client.txt 2011-12-01 14:01:29 +0000 | |||
1336 | +++ man/landscape-client.txt 2012-03-06 09:20:24 +0000 | |||
1337 | @@ -17,6 +17,8 @@ | |||
1338 | 17 | -h, --help Show this help message and exit. | 17 | -h, --help Show this help message and exit. |
1339 | 18 | -c FILE, --config=FILE Use config from this file (any command line | 18 | -c FILE, --config=FILE Use config from this file (any command line |
1340 | 19 | options override settings from the file). | 19 | options override settings from the file). |
1341 | 20 | --bus=BUS Which DBUS bus to use. One of 'session' or | ||
1342 | 21 | 'system'. | ||
1343 | 20 | -d PATH, --data-path=PATH The directory to store data files in. | 22 | -d PATH, --data-path=PATH The directory to store data files in. |
1344 | 21 | -q, --quiet Do not log to the standard output. | 23 | -q, --quiet Do not log to the standard output. |
1345 | 22 | -l FILE, --log-dir=FILE The directory to write log files to. | 24 | -l FILE, --log-dir=FILE The directory to write log files to. |
1346 | 23 | 25 | ||
1347 | === modified file 'man/landscape-config.1' | |||
1348 | --- man/landscape-config.1 2011-12-02 09:52:48 +0000 | |||
1349 | +++ man/landscape-config.1 2012-03-06 09:20:24 +0000 | |||
1350 | @@ -1,5 +1,5 @@ | |||
1351 | 1 | .\"Text automatically generated by txt2man | 1 | .\"Text automatically generated by txt2man |
1353 | 2 | .TH landscape-config 1 "02 December 2011" "" "" | 2 | .TH landscape-config "18 January 2010" "" "" |
1354 | 3 | .SH NAME | 3 | .SH NAME |
1355 | 4 | \fBlandscape-config \fP- configure the Landscape management client | 4 | \fBlandscape-config \fP- configure the Landscape management client |
1356 | 5 | \fB | 5 | \fB |
1357 | @@ -46,6 +46,11 @@ | |||
1358 | 46 | '/etc/landscape/client.conf'). | 46 | '/etc/landscape/client.conf'). |
1359 | 47 | .TP | 47 | .TP |
1360 | 48 | .B | 48 | .B |
1361 | 49 | \fB--bus\fP=BUS | ||
1362 | 50 | Which DBUS bus to use. One of 'session' or 'system' | ||
1363 | 51 | (default: 'system'). | ||
1364 | 52 | .TP | ||
1365 | 53 | .B | ||
1366 | 49 | \fB-d\fP PATH, \fB--data-path\fP=PATH | 54 | \fB-d\fP PATH, \fB--data-path\fP=PATH |
1367 | 50 | The directory to store data files in (default: | 55 | The directory to store data files in (default: |
1368 | 51 | '/var/lib/landscape/client'). | 56 | '/var/lib/landscape/client'). |
1369 | 52 | 57 | ||
1370 | === modified file 'man/landscape-config.txt' | |||
1371 | --- man/landscape-config.txt 2011-12-01 14:01:29 +0000 | |||
1372 | +++ man/landscape-config.txt 2012-03-06 09:20:24 +0000 | |||
1373 | @@ -28,6 +28,8 @@ | |||
1374 | 28 | -c FILE, --config=FILE Use config from this file (any command line options | 28 | -c FILE, --config=FILE Use config from this file (any command line options |
1375 | 29 | override settings from the file) (default: | 29 | override settings from the file) (default: |
1376 | 30 | '/etc/landscape/client.conf'). | 30 | '/etc/landscape/client.conf'). |
1377 | 31 | --bus=BUS Which DBUS bus to use. One of 'session' or 'system' | ||
1378 | 32 | (default: 'system'). | ||
1379 | 31 | -d PATH, --data-path=PATH The directory to store data files in (default: | 33 | -d PATH, --data-path=PATH The directory to store data files in (default: |
1380 | 32 | '/var/lib/landscape/client'). | 34 | '/var/lib/landscape/client'). |
1381 | 33 | -q, --quiet Do not log to the standard output. | 35 | -q, --quiet Do not log to the standard output. |
1382 | 34 | 36 | ||
1383 | === modified file 'man/landscape-message.1' | |||
1384 | --- man/landscape-message.1 2011-12-02 09:52:48 +0000 | |||
1385 | +++ man/landscape-message.1 2012-03-06 09:20:24 +0000 | |||
1386 | @@ -1,5 +1,5 @@ | |||
1387 | 1 | .\"Text automatically generated by txt2man | 1 | .\"Text automatically generated by txt2man |
1389 | 2 | .TH landscape-message 1 "02 December 2011" "" "" | 2 | .TH landscape-message "18 January 2010" "" "" |
1390 | 3 | .SH NAME | 3 | .SH NAME |
1391 | 4 | \fBlandscape-message \fP- Send a message to the landscape web interface | 4 | \fBlandscape-message \fP- Send a message to the landscape web interface |
1392 | 5 | \fB | 5 | \fB |
1393 | @@ -31,6 +31,10 @@ | |||
1394 | 31 | .B | 31 | .B |
1395 | 32 | \fB-h\fP, \fB--help\fP | 32 | \fB-h\fP, \fB--help\fP |
1396 | 33 | Show this help message and exit. | 33 | Show this help message and exit. |
1397 | 34 | .TP | ||
1398 | 35 | .B | ||
1399 | 36 | \fB-b\fP BUS, \fB--bus\fP=BUS | ||
1400 | 37 | The DBUS bus to use to send the message. | ||
1401 | 34 | .SH EXAMPLES | 38 | .SH EXAMPLES |
1402 | 35 | 39 | ||
1403 | 36 | \fBlandscape-message\fP Hello administrator | 40 | \fBlandscape-message\fP Hello administrator |
1404 | 37 | 41 | ||
1405 | === modified file 'man/landscape-message.txt' | |||
1406 | --- man/landscape-message.txt 2011-12-01 14:01:29 +0000 | |||
1407 | +++ man/landscape-message.txt 2012-03-06 09:20:24 +0000 | |||
1408 | @@ -16,6 +16,7 @@ | |||
1409 | 16 | OPTIONS | 16 | OPTIONS |
1410 | 17 | --version Show program's version number and exit. | 17 | --version Show program's version number and exit. |
1411 | 18 | -h, --help Show this help message and exit. | 18 | -h, --help Show this help message and exit. |
1412 | 19 | -b BUS, --bus=BUS The DBUS bus to use to send the message. | ||
1413 | 19 | 20 | ||
1414 | 20 | EXAMPLES | 21 | EXAMPLES |
1415 | 21 | 22 | ||
1416 | 22 | 23 | ||
1417 | === added file 'scripts/landscape-dbus-proxy' | |||
1418 | --- scripts/landscape-dbus-proxy 1970-01-01 00:00:00 +0000 | |||
1419 | +++ scripts/landscape-dbus-proxy 2012-03-06 09:20:24 +0000 | |||
1420 | @@ -0,0 +1,82 @@ | |||
1421 | 1 | #!/usr/bin/env python | ||
1422 | 2 | |||
1423 | 3 | import os | ||
1424 | 4 | import dbus | ||
1425 | 5 | import dbus.service | ||
1426 | 6 | import dbus.glib # This as side effects, don't remove it! | ||
1427 | 7 | |||
1428 | 8 | from dbus.service import Object, BusName, method | ||
1429 | 9 | |||
1430 | 10 | from twisted.internet import glib2reactor | ||
1431 | 11 | glib2reactor.install() | ||
1432 | 12 | from twisted.internet import reactor | ||
1433 | 13 | |||
1434 | 14 | from landscape.lib.bpickle import loads | ||
1435 | 15 | from landscape.lib.lock import lock_path, LockError | ||
1436 | 16 | from landscape.reactor import TwistedReactor | ||
1437 | 17 | from landscape.deployment import Configuration | ||
1438 | 18 | from landscape.broker.amp import RemoteBrokerConnector | ||
1439 | 19 | |||
1440 | 20 | |||
1441 | 21 | BUS_NAME = "com.canonical.landscape.Broker" | ||
1442 | 22 | OBJECT_PATH = "/com/canonical/landscape/Broker" | ||
1443 | 23 | |||
1444 | 24 | |||
1445 | 25 | def array_to_string(array): | ||
1446 | 26 | """Convert an L{Array} of L{Byte}s (or integers) to a Python str.""" | ||
1447 | 27 | result = [] | ||
1448 | 28 | for item in array: | ||
1449 | 29 | if item < 0: | ||
1450 | 30 | item = item + 256 | ||
1451 | 31 | result.append(chr(item)) | ||
1452 | 32 | return "".join(result) | ||
1453 | 33 | |||
1454 | 34 | |||
1455 | 35 | class BrokerDBusObject(Object): | ||
1456 | 36 | """A DBus-published object proxying L{RemoteBroker.send_message}. | ||
1457 | 37 | |||
1458 | 38 | It is used when upgrading from a DBus-based version of the Landscape client | ||
1459 | 39 | to the newer AMP-based one, for letting the old package-changer process | ||
1460 | 40 | performing the upgrade communicate with the new version of the client. | ||
1461 | 41 | """ | ||
1462 | 42 | |||
1463 | 43 | bus_name = BUS_NAME | ||
1464 | 44 | object_path = OBJECT_PATH | ||
1465 | 45 | |||
1466 | 46 | def __init__(self, config): | ||
1467 | 47 | super(BrokerDBusObject, self).__init__(BusName( | ||
1468 | 48 | self.bus_name, dbus.SystemBus()), object_path=self.object_path) | ||
1469 | 49 | self.config = config | ||
1470 | 50 | |||
1471 | 51 | @method(BUS_NAME) | ||
1472 | 52 | def send_message(self, message, urgent=True): | ||
1473 | 53 | """Queue the given message in the message exchange.""" | ||
1474 | 54 | message = loads(array_to_string(message)) | ||
1475 | 55 | |||
1476 | 56 | def cb_connected(broker): | ||
1477 | 57 | result = broker.send_message(message, urgent=True) | ||
1478 | 58 | return result.addCallback(cb_done) | ||
1479 | 59 | |||
1480 | 60 | def cb_done(ignored): | ||
1481 | 61 | return reactor.stop() | ||
1482 | 62 | |||
1483 | 63 | twisted_reactor = TwistedReactor() | ||
1484 | 64 | connector = RemoteBrokerConnector(twisted_reactor, self.config) | ||
1485 | 65 | connected = connector.connect() | ||
1486 | 66 | connected.addCallback(cb_connected) | ||
1487 | 67 | |||
1488 | 68 | |||
1489 | 69 | if __name__ == "__main__": | ||
1490 | 70 | config = Configuration() | ||
1491 | 71 | lock_dir = os.path.join(config.data_path, "package") | ||
1492 | 72 | if os.path.isdir(lock_dir): | ||
1493 | 73 | lock_filename = os.path.join(lock_dir, "changer.lock") | ||
1494 | 74 | try: | ||
1495 | 75 | lock_path(lock_filename) | ||
1496 | 76 | except LockError: | ||
1497 | 77 | # The package-changer is running, this means that we're upgrading from | ||
1498 | 78 | # a non-AMP version and that the upgrade is Landscape driven, so let's | ||
1499 | 79 | # expose the DBus broker proxy to give a chance to the package-changer | ||
1500 | 80 | # to send its result message. | ||
1501 | 81 | remote = BrokerDBusObject(config) | ||
1502 | 82 | reactor.run() | ||
1503 | 0 | 83 | ||
1504 | === modified file 'setup.py' | |||
1505 | --- setup.py 2012-03-05 15:22:36 +0000 | |||
1506 | +++ setup.py 2012-03-06 09:20:24 +0000 | |||
1507 | @@ -30,7 +30,8 @@ | |||
1508 | 30 | "scripts/landscape-package-reporter", | 30 | "scripts/landscape-package-reporter", |
1509 | 31 | "scripts/landscape-release-upgrader", | 31 | "scripts/landscape-release-upgrader", |
1510 | 32 | "scripts/landscape-sysinfo", | 32 | "scripts/landscape-sysinfo", |
1512 | 33 | "scripts/landscape-is-cloud-managed"], | 33 | "scripts/landscape-is-cloud-managed", |
1513 | 34 | "scripts/landscape-dbus-proxy"], | ||
1514 | 34 | ext_modules=[Extension("landscape.lib.initgroups", | 35 | ext_modules=[Extension("landscape.lib.initgroups", |
1515 | 35 | ["landscape/lib/initgroups.c"])] | 36 | ["landscape/lib/initgroups.c"])] |
1516 | 36 | ) | 37 | ) |
Everything went fine on my system (Oneiric)
PASSED (successes=2298)
+1!