Merge ~sylvain-pineau/plainbox-provider-snappy:cleanup_duplicates into plainbox-provider-snappy:master
- Git
- lp:~sylvain-pineau/plainbox-provider-snappy
- cleanup_duplicates
- Merge into master
Status: | Merged |
---|---|
Approved by: | Sylvain Pineau |
Approved revision: | fbc0f857c2a32c44791e85fa55c993625d15ed27 |
Merged at revision: | a3e8df7703ff4eaf5742e87e6852fc0776172d2e |
Proposed branch: | ~sylvain-pineau/plainbox-provider-snappy:cleanup_duplicates |
Merge into: | plainbox-provider-snappy:master |
Diff against target: |
7242 lines (+9/-452) 13 files modified
bin/test_bt_keyboard (+1/-1) dev/null (+0/-157) manage.py (+0/-8) requirements/container-tests-provider-snappy (+4/-0) src/EXECUTABLES (+0/-2) src/Makefile (+2/-6) units/bluetooth/jobs.pxu (+0/-26) units/ethernet/jobs.pxu (+0/-29) units/led/jobs.pxu (+0/-45) units/power/jobs.pxu (+0/-30) units/stress/s3s4.pxu (+2/-2) units/stress/stress-ng.pxu (+0/-13) units/usb/usb.pxu (+0/-133) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Devices Certification Bot | Needs Fixing | ||
Sylvain Pineau (community) | Approve | ||
Maciej Kisielewski | Approve | ||
Review via email: mp+326657@code.launchpad.net |
Commit message
Description of the change
Another prerequisite branch to get p-p-s and p-p-c parts in the same snapcraft.yaml.
Much simpler, only removals for this provider
Devices Certification Bot (ce-certification-qa) wrote : | # |
I tried to merge it but there are some problems. Typically you want to merge or rebase and try again.
Devices Certification Bot (ce-certification-qa) wrote : | # |
The merge was fine but running tests failed.
[trusty] starting container
[trusty] (timing) 0.08user 0.01system 0:06.28elapsed 1%CPU (0avgtext+0avgdata 16636maxresident)k
[trusty] (timing) 0inputs+32outputs (0major+
[trusty] provisioning container
[trusty] (timing) 37.27user 14.30system 1:27.76elapsed 58%CPU (0avgtext+0avgdata 69364maxresident)k
[trusty] (timing) 0inputs+
[trusty-testing] Starting tests...
Found a test script: ./requirements/
[trusty-testing] container-
[trusty-testing] stdout: https:/
[trusty-testing] stderr: https:/
[trusty-testing] (timing) Command exited with non-zero status 1
[trusty-testing] (timing) 6.24user 0.51system 0:09.80elapsed 68%CPU (0avgtext+0avgdata 44144maxresident)k
[trusty-testing] (timing) 0inputs+
[trusty-testing] Fixing file permissions in source directory
[trusty-testing] Destroying container
Name: trusty-testing
State: STOPPED
[xenial] starting container
[xenial] (timing) 0.07user 0.02system 0:04.27elapsed 2%CPU (0avgtext+0avgdata 16676maxresident)k
[xenial] (timing) 0inputs+32outputs (0major+
[xenial] provisioning container
[xenial] (timing) 70.78user 19.13system 2:14.92elapsed 66%CPU (0avgtext+0avgdata 107732maxresident)k
[xenial] (timing) 24inputs+
[xenial-testing] Starting tests...
Found a test script: ./requirements/
[xenial-testing] container-
[xenial-testing] stdout: https:/
[xenial-testing] stderr: https:/
[xenial-testing] (timing) Command exited with non-zero status 1
[xenial-testing] (timing) 6.39user 0.55system 0:09.47elapsed 73%CPU (0avgtext+0avgdata 42364maxresident)k
[xenial-testing] (timing) 0inputs+
[xenial-testing] Fixing file permissions in source directory
[xenial-testing] Destroying container
Name: xenial-testing
State: STOPPED
Sylvain Pineau (sylvain-pineau) wrote : | # |
rebeased
Devices Certification Bot (ce-certification-qa) wrote : | # |
The merge was fine but running tests failed.
[trusty] starting container
[trusty] (timing) 0.08user 0.02system 0:05.26elapsed 2%CPU (0avgtext+0avgdata 16704maxresident)k
[trusty] (timing) 0inputs+32outputs (0major+
[trusty] provisioning container
[trusty] (timing) 37.17user 14.38system 1:26.54elapsed 59%CPU (0avgtext+0avgdata 67792maxresident)k
[trusty] (timing) 0inputs+
[trusty-testing] Starting tests...
Found a test script: ./requirements/
[trusty-testing] container-
[trusty-testing] stdout: https:/
[trusty-testing] stderr: https:/
[trusty-testing] (timing) Command exited with non-zero status 1
[trusty-testing] (timing) 6.30user 0.51system 0:09.41elapsed 72%CPU (0avgtext+0avgdata 44748maxresident)k
[trusty-testing] (timing) 0inputs+
[trusty-testing] Fixing file permissions in source directory
[trusty-testing] Destroying container
Name: trusty-testing
State: STOPPED
[xenial] starting container
[xenial] (timing) 0.09user 0.02system 0:04.28elapsed 2%CPU (0avgtext+0avgdata 16692maxresident)k
[xenial] (timing) 0inputs+32outputs (0major+
[xenial] provisioning container
[xenial] (timing) 71.56user 19.38system 2:14.77elapsed 67%CPU (0avgtext+0avgdata 107732maxresident)k
[xenial] (timing) 24inputs+
[xenial-testing] Starting tests...
Found a test script: ./requirements/
[xenial-testing] container-
[xenial-testing] stdout: https:/
[xenial-testing] stderr: https:/
[xenial-testing] (timing) Command exited with non-zero status 1
[xenial-testing] (timing) 6.31user 0.52system 0:09.14elapsed 74%CPU (0avgtext+0avgdata 42456maxresident)k
[xenial-testing] (timing) 0inputs+
[xenial-testing] Fixing file permissions in source directory
[xenial-testing] Destroying container
Name: xenial-testing
State: STOPPED
Preview Diff
1 | diff --git a/bin/ansi_parser b/bin/ansi_parser |
2 | deleted file mode 100755 |
3 | index c7cf26c..0000000 |
4 | --- a/bin/ansi_parser |
5 | +++ /dev/null |
6 | @@ -1,160 +0,0 @@ |
7 | -#!/usr/bin/env python3 |
8 | -import sys |
9 | -from optparse import OptionParser |
10 | - |
11 | - |
12 | -def parse_buffer(input): |
13 | - output = [""] |
14 | - row = -1 |
15 | - col = 0 |
16 | - escape = "" |
17 | - saved = [0, 0] |
18 | - |
19 | - for ch in input: |
20 | - if ord(ch) == 27 or len(escape) > 0: |
21 | - # On ESC |
22 | - if chr(27) in [escape, ch]: |
23 | - escape = "" |
24 | - if ch == "c": |
25 | - output = [""] |
26 | - row = -1 |
27 | - col = 0 |
28 | - saved = [0, 0] |
29 | - elif ch == "D": |
30 | - row += 1 |
31 | - if row == 0: |
32 | - row = -1 |
33 | - output.append("") |
34 | - elif ch == "M": |
35 | - row -= 1 |
36 | - if row < -len(output): |
37 | - output = [""] + output |
38 | - elif ch == "7": |
39 | - saved = [row + len(output), col] |
40 | - elif ch == "8": |
41 | - [row, col] = saved |
42 | - row -= len(output) |
43 | - elif ord(ch) in [27, 91]: |
44 | - escape = ch |
45 | - continue |
46 | - # Just after hitting the extended ESC marker |
47 | - elif escape == "[": |
48 | - escape = "" |
49 | - |
50 | - if ch in "0123456789;": |
51 | - escape += ch |
52 | - continue |
53 | - elif ch in "Hf": |
54 | - opts = escape.split(";") + ["", ""] |
55 | - row = -len(output) + max(0, int("0" + opts[0]) - 1) |
56 | - col = max(0, int("0" + opts[1]) - 1) |
57 | - elif ch in "s": |
58 | - saved = [row + len(output), col] |
59 | - elif ch in "u": |
60 | - [row, col] = saved |
61 | - row -= len(output) |
62 | - elif ch in "K": |
63 | - if escape == "1": |
64 | - output[row] = " " * (col + 1) + output[row][col + 1:] |
65 | - elif escape == "2": |
66 | - output[row] = "" |
67 | - else: |
68 | - output[row] = output[row][:col] |
69 | - elif ch in "J": |
70 | - if len(escape) == 0: |
71 | - output = output[:row] + [""] |
72 | - else: |
73 | - for i in range(row + len(output) + 1): |
74 | - output[i] = "" |
75 | - elif ch in "A": |
76 | - row -= max(1, int("0" + escape.split(";")[0])) |
77 | - if row <= len(output): |
78 | - row = -len(output) |
79 | - elif ch in "B": |
80 | - row += max(1, int("0" + escape.split(";")[0])) |
81 | - while row >= 0: |
82 | - output.append("") |
83 | - row -= 1 |
84 | - elif ch in "C": |
85 | - col += max(1, int("0" + escape.split(";")[0])) |
86 | - elif ch in "D": |
87 | - col = max(0, col - max(1, int("0" + escape.split(";")[0]))) |
88 | - |
89 | - escape = "" |
90 | - continue |
91 | - |
92 | - # Control char |
93 | - if ch in "\r\n\f\t\b": |
94 | - if ch == "\r": |
95 | - col = 0 |
96 | - if ch in "\n\f": |
97 | - row += 1 |
98 | - if row == 0: |
99 | - row = -1 |
100 | - output.append("") |
101 | - col = 0 |
102 | - if ch == "\t": |
103 | - col = (col + 8) & ~7 |
104 | - if ch == "\b": |
105 | - col = max(0, col - 1) |
106 | - continue |
107 | - |
108 | - # Keep to ascii |
109 | - if ord(ch) not in range(32, 127): |
110 | - ch = "?" |
111 | - if len(output[row]) < col: |
112 | - output[row] += " " * (col - len(output[row])) |
113 | - output[row] = output[row][:col] + ch + output[row][col + 1:] |
114 | - col += 1 |
115 | - |
116 | - return "\n".join(output) |
117 | - |
118 | - |
119 | -def parse_file(file): |
120 | - output = file.read() |
121 | - return parse_buffer(output) |
122 | - |
123 | - |
124 | -def parse_filename(filename): |
125 | - file = open(filename) |
126 | - try: |
127 | - output = parse_file(file) |
128 | - finally: |
129 | - file.close() |
130 | - |
131 | - return output |
132 | - |
133 | - |
134 | -def main(args): |
135 | - usage = "Usage: %prog [OPTIONS] [FILE...]" |
136 | - parser = OptionParser(usage=usage) |
137 | - parser.add_option("-o", "--output", |
138 | - metavar="FILE", |
139 | - help="File where to output the result.") |
140 | - (options, args) = parser.parse_args(args) |
141 | - |
142 | - # Write to stdout |
143 | - if not options.output or options.output == "-": |
144 | - output = sys.stdout |
145 | - |
146 | - # Or from given option |
147 | - else: |
148 | - output = open(options.output, "w") |
149 | - |
150 | - # Read from sdin |
151 | - if not args or (len(args) == 1 and args[0] == "-"): |
152 | - output.write(parse_file(sys.stdin)) |
153 | - |
154 | - # Or from filenames given as arguments |
155 | - else: |
156 | - for arg in args: |
157 | - output.write(parse_filename(arg)) |
158 | - |
159 | - if options.output and options.output != "-": |
160 | - output.close() |
161 | - |
162 | - return 0 |
163 | - |
164 | - |
165 | -if __name__ == "__main__": |
166 | - sys.exit(main(sys.argv[1:])) |
167 | diff --git a/bin/bt_helper.py b/bin/bt_helper.py |
168 | deleted file mode 100644 |
169 | index 6255cb7..0000000 |
170 | --- a/bin/bt_helper.py |
171 | +++ /dev/null |
172 | @@ -1,291 +0,0 @@ |
173 | -# Copyright 2016 Canonical Ltd. |
174 | -# Written by: |
175 | -# Maciej Kisielewski <maciej.kisielewski@canonical.com> |
176 | -# |
177 | -# This is free software: you can redistribute it and/or modify |
178 | -# it under the terms of the GNU General Public License version 3, |
179 | -# as published by the Free Software Foundation. |
180 | -# |
181 | -# This file is distributed in the hope that it will be useful, |
182 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
183 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
184 | -# GNU General Public License for more details. |
185 | -# |
186 | -# You should have received a copy of the GNU General Public License |
187 | -# along with this file. If not, see <http://www.gnu.org/licenses/>. |
188 | -""" |
189 | -This module provides a set of abstractions to ease the process of automating |
190 | -typical Bluetooth task like scanning for devices and pairing with them. |
191 | - |
192 | -It talks with BlueZ stack using dbus. |
193 | -""" |
194 | -import logging |
195 | - |
196 | -import dbus |
197 | -import dbus.service |
198 | -import dbus.mainloop.glib |
199 | -from gi.repository import GObject |
200 | - |
201 | -logger = logging.getLogger(__file__) |
202 | -logger.addHandler(logging.StreamHandler()) |
203 | - |
204 | -IFACE = 'org.bluez.Adapter1' |
205 | -ADAPTER_IFACE = 'org.bluez.Adapter1' |
206 | -DEVICE_IFACE = 'org.bluez.Device1' |
207 | -AGENT_IFACE = 'org.bluez.Agent1' |
208 | - |
209 | -dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
210 | - |
211 | -# To get additional Bluetoot CoDs, check |
212 | -# https://www.bluetooth.com/specifications/assigned-numbers/baseband |
213 | -BT_ANY = 0 |
214 | -BT_KEYBOARD = int('0x2540', 16) |
215 | - |
216 | - |
217 | -class BtManager: |
218 | - """ Main point of contact with dbus factoring bt objects. """ |
219 | - def __init__(self, verbose=False): |
220 | - if verbose: |
221 | - logger.setLevel(logging.DEBUG) |
222 | - self._bus = dbus.SystemBus() |
223 | - self._bt_root = self._bus.get_object('org.bluez', '/') |
224 | - self._manager = dbus.Interface( |
225 | - self._bt_root, 'org.freedesktop.DBus.ObjectManager') |
226 | - self._main_loop = GObject.MainLoop() |
227 | - self._register_agent() |
228 | - |
229 | - def _register_agent(self): |
230 | - path = "/bt_helper/agent" |
231 | - BtAgent(self._bus, path) |
232 | - obj = self._bus.get_object('org.bluez', "/org/bluez") |
233 | - agent_manager = dbus.Interface(obj, "org.bluez.AgentManager1") |
234 | - agent_manager.RegisterAgent(path, 'NoInputNoOutput') |
235 | - logger.info("Agent registered") |
236 | - |
237 | - def _get_objects_by_iface(self, iface_name): |
238 | - for path, ifaces in self._manager.GetManagedObjects().items(): |
239 | - if ifaces.get(iface_name): |
240 | - yield self._bus.get_object('org.bluez', path) |
241 | - |
242 | - def get_bt_adapters(self): |
243 | - """Yield BtAdapter objects for each BT adapter found.""" |
244 | - for adapter in self._get_objects_by_iface(ADAPTER_IFACE): |
245 | - yield BtAdapter(dbus.Interface(adapter, ADAPTER_IFACE), self) |
246 | - |
247 | - def get_bt_devices(self, category=BT_ANY, filters={}): |
248 | - """Yields BtDevice objects currently known to the system. |
249 | - |
250 | - filters - specifies the characteristics of that a BT device must have |
251 | - to be yielded. The keys of filters dictionary represent names of |
252 | - parameters (as specified by the bluetooth DBus Api and represented by |
253 | - DBus proxy object), and its values must match proxy values. |
254 | - I.e. {'Paired': False}. For a full list of Parameters see: |
255 | - http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt |
256 | - |
257 | - Note that this function returns objects corresponding to BT devices |
258 | - that were seen last time scanning was done.""" |
259 | - for device in self._get_objects_by_iface(DEVICE_IFACE): |
260 | - obj = self.get_object_by_path(device.object_path)[DEVICE_IFACE] |
261 | - try: |
262 | - if category != BT_ANY: |
263 | - if obj['Class'] != category: |
264 | - continue |
265 | - rejected = False |
266 | - for filter in filters: |
267 | - if obj[filter] != filters[filter]: |
268 | - rejected = True |
269 | - break |
270 | - if rejected: |
271 | - continue |
272 | - yield BtDevice(dbus.Interface(device, DEVICE_IFACE), self) |
273 | - except KeyError as exc: |
274 | - logger.info('Property %s not found on device %s', |
275 | - exc, device.object_path) |
276 | - continue |
277 | - |
278 | - def get_prop_iface(self, obj): |
279 | - return dbus.Interface(self._bus.get_object( |
280 | - 'org.bluez', obj.object_path), 'org.freedesktop.DBus.Properties') |
281 | - |
282 | - def get_object_by_path(self, path): |
283 | - return self._manager.GetManagedObjects()[path] |
284 | - |
285 | - def get_proxy_by_path(self, path): |
286 | - return self._bus.get_object('org.bluez', path) |
287 | - |
288 | - def wait(self): |
289 | - self._main_loop.run() |
290 | - |
291 | - def quit_loop(self): |
292 | - self._main_loop.quit() |
293 | - |
294 | - def ensure_adapters_powered(self): |
295 | - for adapter in self.get_bt_adapters(): |
296 | - adapter.ensure_powered() |
297 | - |
298 | - def scan(self, timeout=10): |
299 | - """Scan for BT devices visible to all adapters.'""" |
300 | - self._bus.add_signal_receiver( |
301 | - interfaces_added, |
302 | - dbus_interface="org.freedesktop.DBus.ObjectManager", |
303 | - signal_name="InterfacesAdded") |
304 | - self._bus.add_signal_receiver( |
305 | - properties_changed, |
306 | - dbus_interface="org.freedesktop.DBus.Properties", |
307 | - signal_name="PropertiesChanged", |
308 | - arg0="org.bluez.Device1", |
309 | - path_keyword="path") |
310 | - for adapter in self._get_objects_by_iface(ADAPTER_IFACE): |
311 | - try: |
312 | - dbus.Interface(adapter, ADAPTER_IFACE).StopDiscovery() |
313 | - except dbus.exceptions.DBusException: |
314 | - pass |
315 | - dbus.Interface(adapter, ADAPTER_IFACE).StartDiscovery() |
316 | - GObject.timeout_add_seconds(timeout, self._scan_timeout) |
317 | - self._main_loop.run() |
318 | - |
319 | - def get_devices(self, timeout=10, rescan=True): |
320 | - """Scan for and list all devices visible to all adapters.""" |
321 | - if rescan: |
322 | - self.scan(timeout) |
323 | - return list(self.get_bt_devices()) |
324 | - |
325 | - def _scan_timeout(self): |
326 | - for adapter in self._get_objects_by_iface(ADAPTER_IFACE): |
327 | - dbus.Interface(adapter, ADAPTER_IFACE).StopDiscovery() |
328 | - self._main_loop.quit() |
329 | - |
330 | - |
331 | -class BtAdapter: |
332 | - def __init__(self, dbus_iface, bt_mgr): |
333 | - self._if = dbus_iface |
334 | - self._bt_mgr = bt_mgr |
335 | - self._prop_if = bt_mgr.get_prop_iface(dbus_iface) |
336 | - |
337 | - def set_bool_prop(self, prop_name, value): |
338 | - self._prop_if.Set(IFACE, prop_name, dbus.Boolean(value)) |
339 | - |
340 | - def ensure_powered(self): |
341 | - """Turn the adapter on, and do nothing if already on.""" |
342 | - powered = self._prop_if.Get(IFACE, 'Powered') |
343 | - logger.info('Powering on {}'.format( |
344 | - self._if.object_path.split('/')[-1])) |
345 | - if powered: |
346 | - logger.info('Device already powered') |
347 | - return |
348 | - try: |
349 | - self.set_bool_prop('Powered', True) |
350 | - logger.info('Powered on') |
351 | - except Exception as exc: |
352 | - logging.error('Failed to power on - {}'.format( |
353 | - exc.get_dbus_message())) |
354 | - |
355 | - |
356 | -class BtDevice: |
357 | - def __init__(self, dbus_iface, bt_mgr): |
358 | - self._if = dbus_iface |
359 | - self._obj = bt_mgr.get_object_by_path( |
360 | - self._if.object_path)[DEVICE_IFACE] |
361 | - self._bt_mgr = bt_mgr |
362 | - self._prop_if = bt_mgr.get_prop_iface(dbus_iface) |
363 | - |
364 | - def __str__(self): |
365 | - return "{} ({})".format(self.name, self.address) |
366 | - |
367 | - def __repr__(self): |
368 | - return "<BtDevice name:{}, address:{}>".format(self.name, self.address) |
369 | - |
370 | - def pair(self): |
371 | - """Pair the device. |
372 | - |
373 | - This function will try pairing with the device and block until device |
374 | - is paired, error occured or default timeout elapsed (whichever comes |
375 | - first). |
376 | - """ |
377 | - self._prop_if.Set(DEVICE_IFACE, 'Trusted', True) |
378 | - self._if.Pair( |
379 | - reply_handler=self._pair_ok, error_handler=self._pair_error) |
380 | - self._bt_mgr.wait() |
381 | - try: |
382 | - self._if.Connect() |
383 | - except dbus.exceptions.DBusException as exc: |
384 | - logging.error('Failed to connect - {}'.format( |
385 | - exc.get_dbus_message())) |
386 | - |
387 | - def unpair(self): |
388 | - self._if.Disconnect() |
389 | - adapter = self._bt_mgr.get_proxy_by_path(self._obj['Adapter']) |
390 | - dbus.Interface(adapter, ADAPTER_IFACE).RemoveDevice(self._if) |
391 | - |
392 | - @property |
393 | - def name(self): |
394 | - return self._obj.get('Name', '<Unnamed>') |
395 | - |
396 | - @property |
397 | - def address(self): |
398 | - return self._obj['Address'] |
399 | - |
400 | - @property |
401 | - def rssi(self): |
402 | - return self._obj.get('RSSI', None) |
403 | - |
404 | - def _pair_ok(self): |
405 | - logger.info('%s successfully paired', self.name) |
406 | - self._bt_mgr.quit_loop() |
407 | - |
408 | - def _pair_error(self, error): |
409 | - logger.warning('Pairing of %s device failed. %s', self.name, error) |
410 | - self._bt_mgr.quit_loop() |
411 | - |
412 | - |
413 | -class Rejected(dbus.DBusException): |
414 | - _dbus_error_name = "org.bluez.Error.Rejected" |
415 | - |
416 | - |
417 | -class BtAgent(dbus.service.Object): |
418 | - """Agent authenticating everything that is possible.""" |
419 | - @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="") |
420 | - def AuthorizeService(self, device, uuid): |
421 | - logger.info("AuthorizeService (%s, %s)", device, uuid) |
422 | - |
423 | - @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="u") |
424 | - def RequestPasskey(self, device): |
425 | - logger.info("RequestPasskey (%s)", device) |
426 | - passkey = input("Enter passkey: ") |
427 | - return dbus.UInt32(passkey) |
428 | - |
429 | - @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="s") |
430 | - def RequestPinCode(self, device): |
431 | - logger.info("RequestPinCode (%s)", device) |
432 | - return input("Enter PIN Code: ") |
433 | - |
434 | - @dbus.service.method(AGENT_IFACE, in_signature="ouq", out_signature="") |
435 | - def DisplayPasskey(self, device, passkey, entered): |
436 | - print("DisplayPasskey (%s, %06u entered %u)" % |
437 | - (device, passkey, entered), flush=True) |
438 | - |
439 | - @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="") |
440 | - def DisplayPinCode(self, device, pincode): |
441 | - logger.info("DisplayPinCode (%s, %s)", device, pincode) |
442 | - print('Type following pin on your device: {} and press ENTER'.format( |
443 | - pincode), flush=True) |
444 | - |
445 | - @dbus.service.method(AGENT_IFACE, in_signature="ou", out_signature="") |
446 | - def RequestConfirmation(self, device, passkey): |
447 | - logger.info("RequestConfirmation (%s, %06d)", device, passkey) |
448 | - |
449 | - @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="") |
450 | - def RequestAuthorization(self, device): |
451 | - logger.info("RequestAuthorization (%s)", device) |
452 | - |
453 | - @dbus.service.method(AGENT_IFACE, in_signature="", out_signature="") |
454 | - def Cancel(self): |
455 | - logger.info("Cancelled") |
456 | - |
457 | - |
458 | -def properties_changed(interface, changed, invalidated, path): |
459 | - logger.info('Property changed for device @ %s. Change: %s', path, changed) |
460 | - |
461 | - |
462 | -def interfaces_added(path, interfaces): |
463 | - logger.info('Added new bt interfaces: %s @ %s', interfaces, path) |
464 | diff --git a/bin/cpu_offlining b/bin/cpu_offlining |
465 | deleted file mode 100755 |
466 | index 0e88af1..0000000 |
467 | --- a/bin/cpu_offlining |
468 | +++ /dev/null |
469 | @@ -1,46 +0,0 @@ |
470 | -#!/bin/bash |
471 | - |
472 | -echo "Beginning CPU Offlining Test" 1>&2 |
473 | - |
474 | -result=0 |
475 | -cpu_count=0 |
476 | - |
477 | -# Turn CPU cores off |
478 | -for cpu_num in `ls /sys/devices/system/cpu | grep -o cpu[0-9]*`; do |
479 | - if [ -f /sys/devices/system/cpu/$cpu_num/online ]; then |
480 | - if [ "$cpu_num" != "cpu0" ]; then |
481 | - ((cpu_count++)) |
482 | - echo "Offlining $cpu_num" 1>&2 |
483 | - echo 0 > /sys/devices/system/cpu/$cpu_num/online |
484 | - |
485 | - grep -w -i -q $cpu_num /proc/interrupts |
486 | - if [ $? -eq 0 ]; then |
487 | - echo "ERROR: Failed to offline $cpu_num" 1>&2 |
488 | - result=1 |
489 | - fi |
490 | - fi |
491 | - fi |
492 | -done |
493 | - |
494 | -# Back on again |
495 | -for cpu_num in `ls /sys/devices/system/cpu | grep -o cpu[0-9]*`; do |
496 | - if [ -f /sys/devices/system/cpu/$cpu_num/online ]; then |
497 | - if [ "$cpu_num" != "cpu0" ]; then |
498 | - echo "Onlining $cpu_num" 1>&2 |
499 | - echo 1 > /sys/devices/system/cpu/$cpu_num/online |
500 | - grep -w -i -q $cpu_num /proc/interrupts |
501 | - if [ $? -eq 1 ]; then |
502 | - echo "ERROR: Failed to online $cpu_num" 1>&2 |
503 | - result=1 |
504 | - fi |
505 | - fi |
506 | - fi |
507 | -done |
508 | - |
509 | -if [ $result -eq 0 ]; then |
510 | - echo "Successfully turned $cpu_count cores off and back on" |
511 | -else |
512 | - echo "Error with offlining one or more cores. CPU offline may not work if this is an ARM system." 1>&2 |
513 | -fi |
514 | - |
515 | -exit $result |
516 | diff --git a/bin/cpu_topology b/bin/cpu_topology |
517 | deleted file mode 100755 |
518 | index 78317b7..0000000 |
519 | --- a/bin/cpu_topology |
520 | +++ /dev/null |
521 | @@ -1,115 +0,0 @@ |
522 | -#!/usr/bin/env python3 |
523 | -''' |
524 | -cpu_topology |
525 | -Written by Jeffrey Lane <jeffrey.lane@canonical.com> |
526 | -''' |
527 | -import sys |
528 | -import os |
529 | -import re |
530 | - |
531 | - |
532 | -class proc_cpuinfo(): |
533 | - ''' |
534 | - Class to get and handle information from /proc/cpuinfo |
535 | - Creates a dictionary of data gleaned from that file. |
536 | - ''' |
537 | - def __init__(self): |
538 | - self.cpuinfo = {} |
539 | - cpu_fh = open('/proc/cpuinfo', 'r') |
540 | - try: |
541 | - temp = cpu_fh.readlines() |
542 | - finally: |
543 | - cpu_fh.close() |
544 | - |
545 | - r_s390 = re.compile("processor [0-9]") |
546 | - r_x86 = re.compile("processor\s+:") |
547 | - for i in temp: |
548 | - # Handle s390 first |
549 | - if r_s390.match(i): |
550 | - cpu_num = i.split(':')[0].split()[1].strip() |
551 | - key = 'cpu' + cpu_num |
552 | - self.cpuinfo[key] = {'core_id': cpu_num, |
553 | - 'physical_package_id': cpu_num} |
554 | - elif r_x86.match(i): |
555 | - key = 'cpu' + (i.split(':')[1].strip()) |
556 | - self.cpuinfo[key] = {'core_id': '', 'physical_package_id': ''} |
557 | - elif i.startswith('core id'): |
558 | - self.cpuinfo[key].update({'core_id': i.split(':')[1].strip()}) |
559 | - elif i.startswith('physical id'): |
560 | - self.cpuinfo[key].update({'physical_package_id': |
561 | - i.split(':')[1].strip()}) |
562 | - else: |
563 | - continue |
564 | - |
565 | - |
566 | -class sysfs_cpu(): |
567 | - ''' |
568 | - Class to get and handle information from sysfs as relates to CPU topology |
569 | - Creates an informational class to present information on various CPUs |
570 | - ''' |
571 | - |
572 | - def __init__(self, proc): |
573 | - self.syscpu = {} |
574 | - self.path = '/sys/devices/system/cpu/' + proc + '/topology' |
575 | - items = ['core_id', 'physical_package_id'] |
576 | - for i in items: |
577 | - try: |
578 | - syscpu_fh = open(os.path.join(self.path, i), 'r') |
579 | - except OSError as e: |
580 | - print("ERROR: %s" % e) |
581 | - sys.exit(1) |
582 | - else: |
583 | - self.syscpu[i] = syscpu_fh.readline().strip() |
584 | - syscpu_fh.close() |
585 | - |
586 | - |
587 | -def compare(proc_cpu, sys_cpu): |
588 | - cpu_map = {} |
589 | - ''' |
590 | - If there is only 1 CPU the test don't look for core_id |
591 | - and physical_package_id because those information are absent in |
592 | - /proc/cpuinfo on singlecore system |
593 | - ''' |
594 | - for key in proc_cpu.keys(): |
595 | - if 'cpu1' not in proc_cpu: |
596 | - cpu_map[key] = True |
597 | - else: |
598 | - for subkey in proc_cpu[key].keys(): |
599 | - if proc_cpu[key][subkey] == sys_cpu[key][subkey]: |
600 | - cpu_map[key] = True |
601 | - else: |
602 | - cpu_map[key] = False |
603 | - return cpu_map |
604 | - |
605 | - |
606 | -def main(): |
607 | - cpuinfo = proc_cpuinfo() |
608 | - sys_cpu = {} |
609 | - keys = cpuinfo.cpuinfo.keys() |
610 | - for k in keys: |
611 | - sys_cpu[k] = sysfs_cpu(k).syscpu |
612 | - cpu_map = compare(cpuinfo.cpuinfo, sys_cpu) |
613 | - if False in cpu_map.values() or len(cpu_map) < 1: |
614 | - print("FAIL: CPU Topology is incorrect", file=sys.stderr) |
615 | - print("-" * 52, file=sys.stderr) |
616 | - print("{0}{1}".format("/proc/cpuinfo".center(30), "sysfs".center(25)), |
617 | - file=sys.stderr) |
618 | - print("{0}{1}{2}{3}{1}{2}".format( |
619 | - "CPU".center(6), |
620 | - "Physical ID".center(13), |
621 | - "Core ID".center(9), |
622 | - "|".center(3)), file=sys.stderr) |
623 | - for key in sorted(sys_cpu.keys()): |
624 | - print("{0}{1}{2}{3}{4}{5}".format( |
625 | - key.center(6), |
626 | - cpuinfo.cpuinfo[key]['physical_package_id'].center(13), |
627 | - cpuinfo.cpuinfo[key]['core_id'].center(9), |
628 | - "|".center(3), |
629 | - sys_cpu[key]['physical_package_id'].center(13), |
630 | - sys_cpu[key]['core_id'].center(9)), file=sys.stderr) |
631 | - return 1 |
632 | - else: |
633 | - return 0 |
634 | - |
635 | -if __name__ == '__main__': |
636 | - sys.exit(main()) |
637 | diff --git a/bin/disk_info b/bin/disk_info |
638 | deleted file mode 100755 |
639 | index 5efedbd..0000000 |
640 | --- a/bin/disk_info |
641 | +++ /dev/null |
642 | @@ -1,81 +0,0 @@ |
643 | -#!/usr/bin/env python3 |
644 | -# |
645 | -# This file is part of Checkbox. |
646 | -# |
647 | -# Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications |
648 | -# Industrial Technology Research Institute |
649 | -# Copyright 2016 Canonical Ltd. |
650 | -# |
651 | -# Authors: |
652 | -# Nelson Chu <Nelson.Chu@itri.org.tw> |
653 | -# Jeff Lane <jeff@ubuntu.com> |
654 | -# Sylvain Pineau <sylvain.pineau@canonical.com> |
655 | -# |
656 | -# Checkbox is free software: you can redistribute it and/or modify |
657 | -# it under the terms of the GNU General Public License version 3, |
658 | -# as published by the Free Software Foundation. |
659 | -# |
660 | -# Checkbox is distributed in the hope that it will be useful, |
661 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
662 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
663 | -# GNU General Public License for more details. |
664 | -# |
665 | -# You should have received a copy of the GNU General Public License |
666 | -# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
667 | -"""disk_info utility.""" |
668 | - |
669 | -import re |
670 | -import sys |
671 | -from subprocess import check_output, CalledProcessError |
672 | - |
673 | -from checkbox_support.parsers.udevadm import find_pkname_is_root_mountpoint |
674 | - |
675 | - |
676 | -def main(): |
677 | - """ |
678 | - disk_info. |
679 | - |
680 | - Uses lsblk to gather information about disks seen by the OS. |
681 | - Outputs kernel name, model and size data |
682 | - """ |
683 | - pattern = re.compile('KNAME="(?P<KNAME>.*)" ' |
684 | - 'TYPE="(?P<TYPE>.*)" ' |
685 | - 'SIZE="(?P<SIZE>.*)" ' |
686 | - 'MODEL="(?P<MODEL>.*)" ' |
687 | - 'MOUNTPOINT="(?P<MOUNTPOINT>.*)"') |
688 | - try: |
689 | - lsblk = check_output(["lsblk", "-i", "-n", "-P", "-o", |
690 | - "KNAME,TYPE,SIZE,MODEL,MOUNTPOINT"], |
691 | - universal_newlines=True) |
692 | - except CalledProcessError as e: |
693 | - sys.exit(e) |
694 | - |
695 | - disks = 0 |
696 | - for line in lsblk.splitlines(): |
697 | - m = pattern.match(line) |
698 | - if not m or m.group('TYPE') != 'disk': |
699 | - continue |
700 | - # Only consider MMC block devices if one of their mounted partitions is |
701 | - # root (/) |
702 | - if ( |
703 | - m.group('KNAME').startswith('mmcblk') and not |
704 | - find_pkname_is_root_mountpoint(m.group('KNAME'), lsblk) |
705 | - ): |
706 | - continue |
707 | - disks += 1 |
708 | - model = m.group('MODEL') |
709 | - if not model: |
710 | - model = 'Unknown' |
711 | - print("Name: /dev/{}".format(m.group('KNAME'))) |
712 | - print("\t{:7}\t{}".format('Model:', model)) |
713 | - print("\t{:7}\t{}".format('Size:', m.group('SIZE'))) |
714 | - |
715 | - if not disks: |
716 | - print("No disk information discovered.") |
717 | - return 10 |
718 | - |
719 | - return 0 |
720 | - |
721 | - |
722 | -if __name__ == '__main__': |
723 | - sys.exit(main()) |
724 | diff --git a/bin/disk_read_performance_test b/bin/disk_read_performance_test |
725 | deleted file mode 100755 |
726 | index e2a219d..0000000 |
727 | --- a/bin/disk_read_performance_test |
728 | +++ /dev/null |
729 | @@ -1,74 +0,0 @@ |
730 | -#!/bin/bash |
731 | -# |
732 | -# Verify that disk storage performs at or above baseline performance |
733 | -# |
734 | - |
735 | -#Default to a lower bound of 15 MB/s |
736 | -DEFAULT_BUF_READ=15 |
737 | - |
738 | -for disk in $@; do |
739 | - |
740 | - echo "Beginning $0 test for $disk" |
741 | - echo "---------------------------------------------------" |
742 | - |
743 | - disk_type=`udevadm info --name /dev/$disk --query property | grep "ID_BUS" | awk '{gsub(/ID_BUS=/," ")}{printf $1}'` |
744 | - dev_path=`udevadm info --name /dev/$disk --query property | grep "DEVPATH" | awk '{gsub(/DEVPATH=/," ")}{printf $1}'` |
745 | - echo "INFO: $disk type is $disk_type" |
746 | - |
747 | - case $disk_type in |
748 | - "usb" ) |
749 | - #Custom metrics are guesstimates for now... |
750 | - MIN_BUF_READ=7 |
751 | - |
752 | - # Increase MIN_BUF_READ if a USB3 device is plugged in a USB3 hub port |
753 | - if [[ $dev_path =~ ((.*usb[0-9]+).*\/)[0-9]-[0-9\.:\-]+\/.* ]]; then |
754 | - device_version=`cat '/sys/'${BASH_REMATCH[1]}'/version'` |
755 | - hub_port_version=`cat '/sys/'${BASH_REMATCH[2]}'/version'` |
756 | - if [ $(echo "$device_version >= 3.00"|bc -l) -eq 1 -a $(echo "$hub_port_version >= 3.00"|bc -l) -eq 1 ]; then |
757 | - MIN_BUF_READ=80 |
758 | - fi |
759 | - fi |
760 | - ;; |
761 | - "ide" ) MIN_BUF_READ=40;; |
762 | - * ) MIN_BUF_READ=$DEFAULT_BUF_READ;; |
763 | - esac |
764 | - echo "INFO: $disk_type: Using $MIN_BUF_READ MB/sec as the minimum throughput speed" |
765 | - |
766 | - max_speed=0 |
767 | - echo "" |
768 | - echo "Beginning hdparm timing runs" |
769 | - echo "---------------------------------------------------" |
770 | - |
771 | - for iteration in `seq 1 10`; do |
772 | - speed=`hdparm -t /dev/$disk 2>/dev/null | grep "Timing buffered disk reads" | awk -F"=" '{print $2}' | awk '{print $1}'` |
773 | - echo "INFO: Iteration $iteration: Detected speed is $speed MB/sec" |
774 | - |
775 | - if [ -z "$speed" ]; then |
776 | - echo "WARNING: Device $disk is too small! Aborting test." |
777 | - exit 0 |
778 | - fi |
779 | - |
780 | - speed=${speed/.*} |
781 | - if [ $speed -gt $max_speed ]; then |
782 | - max_speed=$speed |
783 | - fi |
784 | - done |
785 | - echo "INFO: Maximum detected speed is $max_speed MB/sec" |
786 | - echo "---------------------------------------------------" |
787 | - echo "" |
788 | - result=0 |
789 | - if [ $max_speed -gt $MIN_BUF_READ ]; then |
790 | - echo "PASS: $disk Max Speed of $max_speed MB/sec is faster than Minimum Buffer Read Speed of $MIN_BUF_READ MB/sec" |
791 | - else |
792 | - echo "FAIL: $disk Max Speed of $max_speed MB/sec is slower than Minimum Buffer Read Speed of $MIN_BUF_READ MB/sec" |
793 | - result=1 |
794 | - fi |
795 | -done |
796 | - |
797 | -if [ $result -gt 0 ]; then |
798 | - echo "WARNING: One or more disks failed testing!" |
799 | - exit 1 |
800 | -else |
801 | - echo "All devices passed testing!" |
802 | - exit 0 |
803 | -fi |
804 | diff --git a/bin/disk_stats_test b/bin/disk_stats_test |
805 | deleted file mode 100755 |
806 | index 851dff7..0000000 |
807 | --- a/bin/disk_stats_test |
808 | +++ /dev/null |
809 | @@ -1,68 +0,0 @@ |
810 | -#!/bin/bash |
811 | - |
812 | -#Simple script to gather some data about a disk to verify it's seen by the OS |
813 | -#and is properly represented. Defaults to sda if not passed a disk at run time |
814 | - |
815 | -DISK="sda" |
816 | - |
817 | -check_return_code() { |
818 | - if [ "${1}" -ne "0" ]; then |
819 | - echo "ERROR: retval ${1} : ${2}" >&2 |
820 | - exit ${1} |
821 | - fi |
822 | -} |
823 | - |
824 | -if [[ "$1" != '' ]]; then |
825 | - DISK="$1" |
826 | -fi |
827 | - |
828 | -#Get some baseline stats for use later |
829 | -echo "Getting baseline stats" |
830 | -PROC_STAT_BEGIN=`grep -m 1 $DISK /proc/diskstats` |
831 | -SYS_STAT_BEGIN=`cat /sys/block/$DISK/stat` |
832 | - |
833 | -#Generate some disk activity using hdparm -t |
834 | -echo "Generating some disk activity" |
835 | -hdparm -t "/dev/$DISK" 2&> /dev/null |
836 | - |
837 | -#Sleep 5 to let the stats files catch up |
838 | -sleep 5 |
839 | - |
840 | -#Check /proc/partitions, exit with fail if disk isn't found |
841 | -echo "Checking /proc/partitions" |
842 | -grep -q $DISK /proc/partitions |
843 | -check_return_code $? "Disk $DISK not found in /proc/partitions" |
844 | - |
845 | -#Next, check /proc/diskstats |
846 | -echo "Checking /proc/diskstats" |
847 | -grep -q -m 1 $DISK /proc/diskstats |
848 | -check_return_code $? "Disk $DISK not found in /proc/diskstats" |
849 | - |
850 | -#Verify the disk shows up in /sys/block/ |
851 | -echo "Checking /sys/block" |
852 | -ls /sys/block | grep -q $DISK |
853 | -check_return_code $? "Disk $DISK not found in /sys/block" |
854 | - |
855 | -#Verify there are stats in /sys/block/$DISK/stat |
856 | -echo "Checking /sys/block/$DISK/stat" |
857 | -[[ -s "/sys/block/$DISK/stat" ]] |
858 | -check_return_code $? "stat is either empty or nonexistant in /sys/block/$DISK/" |
859 | - |
860 | -#Sleep 5 to let the stats files catch up |
861 | -sleep 5 |
862 | - |
863 | -#Make sure the stats have changed: |
864 | -echo "Getting ending stats" |
865 | -PROC_STAT_END=`grep -m 1 $DISK /proc/diskstats` |
866 | -SYS_STAT_END=`cat /sys/block/$DISK/stat` |
867 | - |
868 | -echo "Checking /proc/diskstats for changes" |
869 | -[[ "$PROC_STAT_BEGIN" != "$PROC_STAT_END" ]] |
870 | -check_return_code $? "Stats in /proc/diskstats did not change" |
871 | - |
872 | -echo "Checking /sys/block/$DISK/stat for changes" |
873 | -[[ "$SYS_STAT_BEGIN" != "$SYS_STAT_END" ]] |
874 | -check_return_code $? "Stats in /sys/block/$DISK/stat did not change" |
875 | - |
876 | -echo "PASS: Finished testing stats for $DISK" |
877 | -exit 0 |
878 | diff --git a/bin/disk_stress_ng b/bin/disk_stress_ng |
879 | deleted file mode 100755 |
880 | index 8445488..0000000 |
881 | --- a/bin/disk_stress_ng |
882 | +++ /dev/null |
883 | @@ -1,242 +0,0 @@ |
884 | -#!/bin/bash |
885 | - |
886 | -# Script to disk stress tests using stress-ng |
887 | -# |
888 | -# Copyright (c) 2016 Canonical Ltd. |
889 | -# |
890 | -# Authors |
891 | -# Rod Smith <rod.smith@canonical.com> |
892 | -# |
893 | -# This program is free software: you can redistribute it and/or modify |
894 | -# it under the terms of the GNU General Public License version 3, |
895 | -# as published by the Free Software Foundation. |
896 | -# |
897 | -# This program is distributed in the hope that it will be useful, |
898 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
899 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
900 | -# GNU General Public License for more details. |
901 | -# |
902 | -# You should have received a copy of the GNU General Public License |
903 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
904 | -# |
905 | -# The purpose of this script is to run disk stress tests using the |
906 | -# stress-ng program. |
907 | -# |
908 | -# Usage: |
909 | -# disk_stress_ng [ <device-filename> ] |
910 | -# [ --base-time <time-in-seconds> ] |
911 | -# [ --really-run ] |
912 | -# |
913 | -# Parameters: |
914 | -# --disk-device -- This is the WHOLE-DISK device filename WITHOUT "/dev/" |
915 | -# (e.g., sda). The script finds a filesystem on that |
916 | -# device, mounts it if necessary, and runs the tests on |
917 | -# that mounted filesystem. |
918 | -# Test with iostat |
919 | - |
920 | -get_params() { |
921 | - disk_device="/dev/sda" |
922 | - short_device="sda" |
923 | - base_time="240" |
924 | - really_run="N" |
925 | - while [ $# -gt 0 ] ; do |
926 | - case $1 in |
927 | - --base-time) base_time="$2" |
928 | - shift |
929 | - ;; |
930 | - --really-run) really_run="Y" |
931 | - ;; |
932 | - *) disk_device="/dev/$1" |
933 | - disk_device=`echo $disk_device | sed "s/\/dev\/\/dev/\/dev/g"` |
934 | - short_device=$(echo $disk_device | sed "s/\/dev//g") |
935 | - if [ ! -b $disk_device ] ; then |
936 | - echo "Unknown block device \"$disk_device\"" |
937 | - echo "Usage: $0 [ --base-time <time-in-seconds> ] [ --really-run ]" |
938 | - echo " [ device-file ]" |
939 | - exit 1 |
940 | - fi |
941 | - ;; |
942 | - esac |
943 | - shift |
944 | - done |
945 | - mounted_part="N" |
946 | -} # get_params() |
947 | - |
948 | - |
949 | -# Find the largest partition that holds a supported filesystem on $disk_device. |
950 | -# Output: |
951 | -# $largest_part -- Device filename of largest qualifying partition |
952 | -# $largest_size -- Size of largest qualifying partition |
953 | -# $largest_fs -- Filesystem (ext4, etc.) used on largest qualifying partition |
954 | -# $unsupported_fs -- Empty or contains name of unsupported filesystem found on disk |
955 | -find_largest_partition() { |
956 | - largest_part="" |
957 | - largest_size=0 |
958 | - partitions=$(lsblk -b -l -n -o NAME,SIZE,TYPE,MOUNTPOINT $disk_device | grep part | tr -s " ") |
959 | - unsupported_fs="" |
960 | - for partition in $(echo "$partitions" | cut -d " " -f 1) ; do |
961 | - part_size=$(echo "$partitions" | grep "$partition " | cut -d " " -f 2) |
962 | - local blkid_info=$(blkid -s TYPE /dev/$partition | grep -E ext2\|ext3\|ext4\|xfs\|jfs\|btrfs) |
963 | - if [ "$part_size" -gt "$largest_size" ] && [ -n "$blkid_info" ] ; then |
964 | - largest_size=$part_size |
965 | - largest_part="/dev/$partition" |
966 | - largest_fs=$(blkid -s TYPE "/dev/$partition" | cut -d "=" -f 2) |
967 | - fi |
968 | - local blkid_info=$(blkid -s TYPE /dev/$partition | grep -E ntfs\|vfat\|hfs) |
969 | - if [ -n "$blkid_info" ] ; then |
970 | - # If there's an NTFS, HFS+, or FAT filesystem on the disk make note of it.... |
971 | - unsupported_fs=$(blkid -s TYPE "/dev/$partition" | cut -d "=" -f 2) |
972 | - fi |
973 | - done |
974 | -} # find_largest_partition() |
975 | - |
976 | -# Find the largest filesystem on $disk_device. If that partition is not |
977 | -# already mounted, try to mount it. |
978 | -# Output: |
979 | -# $test_dir -- Directory in which tests will occur |
980 | -# $mount_point -- Location where filesystem is mounted |
981 | -# $mounted_part -- Sets to "Y" if script mounted partition |
982 | -# $made_mountpoint -- Sets to "Y" if script created the mount point |
983 | -mount_filesystem() { |
984 | - test_dir="/tmp/disk_stress_ng" |
985 | - if [ -b $disk_device ] |
986 | - then |
987 | - echo "$disk_device is a block device" |
988 | - |
989 | - #Add a check for warnings |
990 | - WARN=$(parted -s ${disk_device} print | grep "^Warning.*${disk}.*[Rr]ead-only" 2>&1) |
991 | - if [[ $? == 0 ]] |
992 | - then |
993 | - echo "Warning found in parted output:" |
994 | - echo $WARN |
995 | - echo "Aborting Test" |
996 | - exit 1 |
997 | - fi |
998 | - else |
999 | - echo "$disk_device is not a block device! Aborting!" |
1000 | - exit 1 |
1001 | - fi |
1002 | - |
1003 | - find_largest_partition |
1004 | - |
1005 | - if [ -n "$largest_part" ] ; then |
1006 | - echo "Found largest partition: \"$largest_part\"" |
1007 | - mount_point=$(df | grep "$largest_part " | tr -s " " | cut -d " " -f 6) |
1008 | - if [ "$mount_point" == "" ] && [ "$really_run" == "Y" ] ; then |
1009 | - disk_device=$(echo $disk_device | sed "s/\/dev\/\/dev/\/dev/g") |
1010 | - mount_point="/mnt$short_device" |
1011 | - echo "No partition is mounted from $disk_device; attempting to mount one...." |
1012 | - if [ ! -d $mount_point ] ; then |
1013 | - mkdir -p "$mount_point" |
1014 | - made_mountpoint="Y" |
1015 | - fi |
1016 | - mount "$largest_part" "$mount_point" |
1017 | - mounted_part="Y" |
1018 | - fi |
1019 | - if [ "$mount_point" == "/" ] ; then |
1020 | - test_dir="/tmp/disk_stress_ng" |
1021 | - else |
1022 | - test_dir="$mount_point/tmp/disk_stress_ng" |
1023 | - fi |
1024 | - echo "Test will use $largest_part, mounted at \"$mount_point\", using $largest_fs" |
1025 | - else |
1026 | - echo "There appears to be no partition with a suitable filesystem" |
1027 | - echo "on $disk_device; please create a suitable partition and re-run" |
1028 | - echo "this test." |
1029 | - if [ -n "unsupported_fs" ] ; then |
1030 | - echo "NOTE: A filesystem of type $unsupported_fs was found, but is not supported" |
1031 | - echo "by this test. A Linux-native filesystem (ext2/3/4fs, XFS, JFS, or Btrfs)" |
1032 | - echo "is required." |
1033 | - fi |
1034 | - exit 1 |
1035 | - fi |
1036 | -} # mount_filesystem() |
1037 | - |
1038 | - |
1039 | -# Run an individual stressor |
1040 | -# Input: |
1041 | -# $1 = stressor name (e.g., copyfile, dentry) |
1042 | -# $2 = run time |
1043 | -# Output: |
1044 | -# had_error -- sets to "1" if an error occurred |
1045 | -run_stressor() { |
1046 | - local runtime="$2" |
1047 | - # Multiply runtime by 5; will forcefully terminate if stress-ng |
1048 | - # fails to return in that time. |
1049 | - end_time=$((runtime*5)) |
1050 | - echo "Running stress-ng $1 stressor for $2 seconds...." |
1051 | - # Use "timeout" command to launch stress-ng, to catch it should it go into |
1052 | - # la-la land |
1053 | - timeout -s 9 $end_time stress-ng --aggressive --verify --timeout $runtime \ |
1054 | - --temp-path $test_dir --$1 0 |
1055 | - return_code="$?" |
1056 | - echo "return_code is $return_code" |
1057 | - if [ "$return_code" != "0" ] ; then |
1058 | - had_error=1 |
1059 | - echo "*****************************************************************" |
1060 | - if [ $return_code = "137" ] ; then |
1061 | - echo "** stress-ng disk test timed out and was forcefully terminated!" |
1062 | - else |
1063 | - echo "** Error $return_code reported on stressor $stressor!)" |
1064 | - fi |
1065 | - echo "*****************************************************************" |
1066 | - had_error=1 |
1067 | - result=$return_code |
1068 | - fi |
1069 | -} # run_stressor() |
1070 | - |
1071 | - |
1072 | -# |
1073 | -# Main program body.... |
1074 | -# |
1075 | - |
1076 | - |
1077 | -get_params "$@" |
1078 | -mount_filesystem |
1079 | -echo "test_dir is $test_dir" |
1080 | - |
1081 | -had_error=0 |
1082 | - |
1083 | -# Tests Colin said to try but that aren't present as of standard stress-ng |
1084 | -# in Ubuntu 16.04: |
1085 | -# |
1086 | -# "chown" "copyfile" "ioprio" "locka" "lockofd" "madvise" "msync" "seal" |
1087 | -# |
1088 | -# TODO: Consider adding these tests for Ubuntu 18.04, or ealier with an |
1089 | -# updated stress-ng in the certification PPA.... |
1090 | - |
1091 | -disk_stressors=("aio" "aiol" "chdir" "chmod" "dentry" "dir" "fallocate" \ |
1092 | - "fiemap" "filename" "flock" "fstat" "hdd" "lease" "lockf" \ |
1093 | - "mknod" "readahead" "seek" "sync-file" "xattr") |
1094 | - |
1095 | -total_runtime=$((${#disk_stressors[@]}*$base_time)) |
1096 | - |
1097 | -echo "Estimated total run time is $total_runtime seconds" |
1098 | -echo "" |
1099 | - |
1100 | -if [ "$really_run" == "Y" ] ; then |
1101 | - mkdir -p "$test_dir" |
1102 | - for stressor in ${disk_stressors[@]}; do |
1103 | - run_stressor $stressor $base_time |
1104 | - done |
1105 | - rm -rf "$test_dir" |
1106 | - if [ "$mounted_part" == "Y" ] ; then |
1107 | - umount "$mount_point" |
1108 | - if [ "$made_mountpoint" == "Y" ] ; then |
1109 | - rmdir "$mount_point" |
1110 | - fi |
1111 | - fi |
1112 | -else |
1113 | - echo "To actually run tests, pass the --really-run option." |
1114 | - echo "Script is now terminating...." |
1115 | - exit 1 |
1116 | -fi |
1117 | - |
1118 | -echo "*******************************************************************" |
1119 | -if [ $had_error = "0" ] ; then |
1120 | - echo "** stress-ng disk test passed!" |
1121 | -else |
1122 | - echo "** stress-ng disk test failed; most recent error was $result" |
1123 | -fi |
1124 | -echo "*******************************************************************" |
1125 | -exit $result |
1126 | diff --git a/bin/fwts_test b/bin/fwts_test |
1127 | deleted file mode 100755 |
1128 | index 7672a2b..0000000 |
1129 | --- a/bin/fwts_test |
1130 | +++ /dev/null |
1131 | @@ -1,492 +0,0 @@ |
1132 | -#! /usr/bin/python3 |
1133 | - |
1134 | -import sys |
1135 | -import re |
1136 | -from time import time, sleep |
1137 | -from argparse import ArgumentParser, RawTextHelpFormatter, REMAINDER |
1138 | -from subprocess import Popen, PIPE |
1139 | -from syslog import * |
1140 | -from shutil import which |
1141 | -import os |
1142 | - |
1143 | -# These tests require user interaction and need either special handling |
1144 | -# or skipping altogether (right now, we skip them but they're kept here |
1145 | -# in case we figure out a way to present the interaction to the user). |
1146 | -INTERACTIVE_TESTS = ['ac_adapter', |
1147 | - 'battery', |
1148 | - 'hotkey', |
1149 | - 'power_button', |
1150 | - 'brightness', |
1151 | - 'lid'] |
1152 | -# These are performed on QA certification runs |
1153 | -QA_TESTS = ['acpitests', |
1154 | - 'acpidump', |
1155 | - 'acpitables', |
1156 | - 'apicinstance', |
1157 | - 'aspm', |
1158 | - 'bios32', |
1159 | - 'dmicheck', |
1160 | - 'ebda', |
1161 | - 'mpcheck', |
1162 | - 'msr', |
1163 | - 'nx', |
1164 | - 'version'] |
1165 | -# These are advanced tests that shouldn't affect certification status |
1166 | -NON_CERT_TESTS = ['bios_info', |
1167 | - 'cmosdump', |
1168 | - 'cpufreq', |
1169 | - 'crs', |
1170 | - 'crsdump', |
1171 | - 'csm', |
1172 | - 'ebdadump', |
1173 | - 'fan', |
1174 | - 'gpedump', |
1175 | - 'hda_audio', |
1176 | - 'maxfreq', |
1177 | - 'maxreadreq', |
1178 | - 'memmapdump', |
1179 | - 'microcode', |
1180 | - 'mpdump', |
1181 | - 'os2gap', |
1182 | - 'osilinux', |
1183 | - 'pciirq', |
1184 | - 'plddump', |
1185 | - 'pnp', |
1186 | - 'prsdump', |
1187 | - 'romdump', |
1188 | - 'securebootcert', |
1189 | - 'syntaxcheck', |
1190 | - 'uefidump', |
1191 | - 'uefirtmisc', |
1192 | - 'uefirttime', |
1193 | - 'uefirtvariable', |
1194 | - 'uefivarinfo', |
1195 | - 'wakealarm' |
1196 | - ] |
1197 | -# The following tests will record logs in a separate file for the HWE team |
1198 | -HWE_TESTS = ['mtrr', |
1199 | - 'virt', |
1200 | - 'apicedge', |
1201 | - 'klog', |
1202 | - 'oops', |
1203 | - 'uefibootpath'] |
1204 | -CERT_TESTS = sorted(QA_TESTS + HWE_TESTS) |
1205 | -TESTS = sorted(QA_TESTS + NON_CERT_TESTS + HWE_TESTS) |
1206 | - |
1207 | - |
1208 | -def get_sleep_times(start_marker, end_marker, sleep_time, resume_time): |
1209 | - logfile = '/var/log/syslog' |
1210 | - log_fh = open(logfile, 'r', encoding='UTF-8') |
1211 | - line = '' |
1212 | - run = 'FAIL' |
1213 | - sleep_start_time = 0.0 |
1214 | - sleep_end_time = 0.0 |
1215 | - resume_start_time = 0.0 |
1216 | - resume_end_time = 0.0 |
1217 | - |
1218 | - while start_marker not in line: |
1219 | - try: |
1220 | - line = log_fh.readline() |
1221 | - except UnicodeDecodeError: |
1222 | - continue |
1223 | - if start_marker in line: |
1224 | - loglist = log_fh.readlines() |
1225 | - |
1226 | - for idx in range(0, len(loglist)): |
1227 | - if 'PM: Syncing filesystems' in loglist[idx]: |
1228 | - sleep_start_time = re.split('[\[\]]', loglist[idx])[1].strip() |
1229 | - if 'ACPI: Low-level resume complete' in loglist[idx]: |
1230 | - sleep_end_time = re.split('[\[\]]', loglist[idx - 1])[1].strip() |
1231 | - resume_start_time = re.split('[\[\]]', loglist[idx])[1].strip() |
1232 | - idx += 1 |
1233 | - if 'Restarting tasks' in loglist[idx]: |
1234 | - resume_end_time = re.split('[\[\]]', loglist[idx])[1].strip() |
1235 | - if end_marker in loglist[idx]: |
1236 | - run = 'PASS' |
1237 | - break |
1238 | - |
1239 | - sleep_elapsed = float(sleep_end_time) - float(sleep_start_time) |
1240 | - resume_elapsed = float(resume_end_time) - float(resume_start_time) |
1241 | - return (run, sleep_elapsed, resume_elapsed) |
1242 | - |
1243 | - |
1244 | -def average_times(runs): |
1245 | - sleep_total = 0.0 |
1246 | - resume_total = 0.0 |
1247 | - run_count = 0 |
1248 | - for run in runs.keys(): |
1249 | - run_count += 1 |
1250 | - sleep_total += runs[run][1] |
1251 | - resume_total += runs[run][2] |
1252 | - sleep_avg = sleep_total / run_count |
1253 | - resume_avg = resume_total / run_count |
1254 | - print('Average time to sleep: %0.5f' % sleep_avg) |
1255 | - print('Average time to resume: %0.5f' % resume_avg) |
1256 | - |
1257 | - |
1258 | -def fix_sleep_args(args): |
1259 | - new_args = [] |
1260 | - for arg in args: |
1261 | - if "=" in arg: |
1262 | - new_args.extend(arg.split('=')) |
1263 | - else: |
1264 | - new_args.append(arg) |
1265 | - return new_args |
1266 | - |
1267 | - |
1268 | -def detect_progress_indicator(): |
1269 | - # Return a command suitable for piping progress information to its |
1270 | - # stdin (invoked via Popen), in list format. |
1271 | - # Return zenity if installed and DISPLAY (--auto-close) |
1272 | - # return dialog if installed and no DISPLAY (width height) |
1273 | - display = os.environ.get('DISPLAY') |
1274 | - if display and which('zenity'): |
1275 | - return ["zenity", "--progress", "--text", "Progress", "--auto-close"] |
1276 | - if not display and which('dialog'): |
1277 | - return ["dialog", "--gauge", "Progress", "20", "70"] |
1278 | - # Return empty list if no progress indicator is to be used |
1279 | - return [] |
1280 | - |
1281 | - |
1282 | -def main(): |
1283 | - description_text = 'Tests the system BIOS using the Firmware Test Suite' |
1284 | - epilog_text = ('To perform sleep testing, you will need at least some of ' |
1285 | - 'the following options: \n' |
1286 | - 's3 or s4: tells fwts which type of sleep to perform.\n' |
1287 | - '--s3-delay-delta\n' |
1288 | - '--s3-device-check\n' |
1289 | - '--s3-device-check-delay\n' |
1290 | - '--s3-hybrid-sleep\n' |
1291 | - '--s3-max-delay\n' |
1292 | - '--s3-min-delay\n' |
1293 | - '--s3-multiple\n' |
1294 | - '--s3-quirks\n' |
1295 | - '--s3-sleep-delay\n' |
1296 | - '--s3power-sleep-delay\n\n' |
1297 | - 'Example: fwts_test --sleep s3 --s3-min-delay 30 ' |
1298 | - '--s3-multiple 10 --s3-device-check\n\n' |
1299 | - 'For further help with sleep options:\n' |
1300 | - 'fwts_test --fwts-help') |
1301 | - parser = ArgumentParser(description=description_text, |
1302 | - epilog=epilog_text, |
1303 | - formatter_class=RawTextHelpFormatter) |
1304 | - parser.add_argument('-l', '--log', |
1305 | - default='/tmp/fwts_results.log', |
1306 | - help=('Specify the location and name ' |
1307 | - 'of the log file.\n' |
1308 | - '[Default: %(default)s]')) |
1309 | - parser.add_argument('-f', '--fail-level', |
1310 | - default='high', |
1311 | - choices=['critical', 'high', 'medium', |
1312 | - 'low', 'none', 'aborted'], |
1313 | - help=('Specify the FWTS failure level that will ' |
1314 | - 'trigger this script to return a failing exit ' |
1315 | - 'code. For example, if you chose "critical" as ' |
1316 | - 'the fail-level, this wrapper will NOT return ' |
1317 | - 'a failing exit code unless FWTS reports a ' |
1318 | - 'test as FAILED_CRITICAL. You will still be ' |
1319 | - 'notified of all FWTS test failures. ' |
1320 | - '[Default level: %(default)s]')) |
1321 | - sleep_args = parser.add_argument_group('Sleep Options', |
1322 | - ('The following arguments are to ' |
1323 | - 'only be used with the ' |
1324 | - '--sleep test option')) |
1325 | - sleep_args.add_argument('--sleep-time', |
1326 | - dest='sleep_time', |
1327 | - action='store', |
1328 | - help=('The max time in seconds that a system ' |
1329 | - 'should take\nto completely enter sleep. ' |
1330 | - 'Anything more than this\ntime will cause ' |
1331 | - 'that test iteration to fail.\n' |
1332 | - '[Default: 10s]')) |
1333 | - sleep_args.add_argument('--resume-time', |
1334 | - dest='resume_time', |
1335 | - action='store', |
1336 | - help=('Same as --sleep-time, except this applies ' |
1337 | - 'to the\ntime it takes a system to fully ' |
1338 | - 'wake from sleep.\n[Default: 3s]')) |
1339 | - |
1340 | - group = parser.add_mutually_exclusive_group() |
1341 | - group.add_argument('-t', '--test', |
1342 | - action='append', |
1343 | - help='Name of the test to run.') |
1344 | - group.add_argument('-a', '--all', |
1345 | - action='store_true', |
1346 | - help='Run ALL FWTS automated tests (assumes -w and -c)') |
1347 | - group.add_argument('-s', '--sleep', |
1348 | - nargs=REMAINDER, |
1349 | - action='store', |
1350 | - help=('Perform sleep test(s) using the additional\n' |
1351 | - 'arguments provided after --sleep. Remaining\n' |
1352 | - 'items on the command line will be passed \n' |
1353 | - 'through to fwts for performing sleep tests. \n' |
1354 | - 'For info on these extra fwts options, please \n' |
1355 | - 'see the epilog below and \n' |
1356 | - 'the --fwts-help option.')) |
1357 | - group.add_argument('--hwe', |
1358 | - action='store_true', |
1359 | - help='Run HWE concerned tests in fwts') |
1360 | - group.add_argument('--qa', |
1361 | - action='store_true', |
1362 | - help='Run QA concerned tests in fwts') |
1363 | - group.add_argument('--fwts-help', |
1364 | - dest='fwts_help', |
1365 | - action='store_true', |
1366 | - help='Display the help info for fwts itself (lengthy)') |
1367 | - group.add_argument('--list', |
1368 | - action='store_true', |
1369 | - help='List all tests in fwts.') |
1370 | - group.add_argument('--list-cert', |
1371 | - action='store_true', |
1372 | - help='List all certification tests in fwts.') |
1373 | - group.add_argument('--list-advanced', |
1374 | - action='store_true', |
1375 | - help='List all advanced tests in fwts.') |
1376 | - group.add_argument('--list-hwe', |
1377 | - action='store_true', |
1378 | - help='List all HWE concerned tests in fwts') |
1379 | - group.add_argument('--list-qa', |
1380 | - action='store_true', |
1381 | - help='List all QA concerned tests in fwts') |
1382 | - args = parser.parse_args() |
1383 | - |
1384 | - tests = [] |
1385 | - results = {} |
1386 | - critical_fails = [] |
1387 | - high_fails = [] |
1388 | - medium_fails = [] |
1389 | - low_fails = [] |
1390 | - passed = [] |
1391 | - aborted = [] |
1392 | - |
1393 | - # Set correct fail level |
1394 | - if args.fail_level is not 'none': |
1395 | - args.fail_level = 'FAILED_%s' % args.fail_level.upper() |
1396 | - |
1397 | - # Get our failure priority and create the priority values |
1398 | - fail_levels = {'FAILED_CRITICAL': 4, |
1399 | - 'FAILED_HIGH': 3, |
1400 | - 'FAILED_MEDIUM': 2, |
1401 | - 'FAILED_LOW': 1, |
1402 | - 'FAILED_NONE': 0, |
1403 | - 'FAILED_ABORTED': -1} |
1404 | - fail_priority = fail_levels[args.fail_level] |
1405 | - |
1406 | - # Enforce only using sleep opts with --sleep |
1407 | - if args.sleep_time or args.resume_time and not args.sleep: |
1408 | - parser.error('--sleep-time and --resume-time only apply to the ' |
1409 | - '--sleep testing option.') |
1410 | - if args.fwts_help: |
1411 | - Popen('fwts -h', shell=True).communicate()[0] |
1412 | - return 0 |
1413 | - elif args.list: |
1414 | - print('\n'.join(TESTS)) |
1415 | - return 0 |
1416 | - elif args.list_cert: |
1417 | - print('\n'.join(CERT_TESTS)) |
1418 | - return 0 |
1419 | - elif args.list_advanced: |
1420 | - print('\n'.join(NON_CERT_TESTS)) |
1421 | - return 0 |
1422 | - elif args.list_hwe: |
1423 | - print('\n'.join(HWE_TESTS)) |
1424 | - return 0 |
1425 | - elif args.list_qa: |
1426 | - print('\n'.join(QA_TESTS)) |
1427 | - return 0 |
1428 | - elif args.test: |
1429 | - tests.extend(args.test) |
1430 | - elif args.all: |
1431 | - tests.extend(TESTS) |
1432 | - elif args.hwe: |
1433 | - tests.extend(HWE_TESTS) |
1434 | - elif args.qa: |
1435 | - tests.extend(QA_TESTS) |
1436 | - elif args.sleep: |
1437 | - args.sleep = fix_sleep_args(args.sleep) |
1438 | - iterations = 1 |
1439 | - # if multiple iterations are requested, we need to intercept |
1440 | - # that argument and keep it from being presented to fwts since |
1441 | - # we're handling the iterations directly. |
1442 | - s3 = '--s3-multiple' |
1443 | - s4 = '--s4-multiple' |
1444 | - if s3 in args.sleep: |
1445 | - iterations = int(args.sleep.pop(args.sleep.index(s3) + 1)) |
1446 | - args.sleep.remove(s3) |
1447 | - if s4 in args.sleep: |
1448 | - iterations = int(args.sleep.pop(args.sleep.index(s4) + 1)) |
1449 | - args.sleep.remove(s4) |
1450 | - # if we've passed our custom sleep arguments for resume or sleep |
1451 | - # time, we need to intercept those as well. |
1452 | - resume_time_arg = '--resume-time' |
1453 | - sleep_time_arg = '--sleep-time' |
1454 | - if resume_time_arg in args.sleep: |
1455 | - args.resume_time = int(args.sleep.pop( |
1456 | - args.sleep.index(resume_time_arg) + 1)) |
1457 | - args.sleep.remove(resume_time_arg) |
1458 | - if sleep_time_arg in args.sleep: |
1459 | - args.sleep_time = int(args.sleep.pop( |
1460 | - args.sleep.index(sleep_time_arg) + 1)) |
1461 | - args.sleep.remove(sleep_time_arg) |
1462 | - # if we still haven't set a sleep or resume time, use defauts. |
1463 | - if not args.sleep_time: |
1464 | - args.sleep_time = 15 |
1465 | - if not args.resume_time: |
1466 | - args.resume_time = 15 |
1467 | - tests.extend(args.sleep) |
1468 | - else: |
1469 | - tests.extend(CERT_TESTS) |
1470 | - |
1471 | - # run the tests we want |
1472 | - if args.sleep: |
1473 | - iteration_results = {} |
1474 | - print('=' * 20 + ' Test Results ' + '=' * 20) |
1475 | - progress_indicator = None |
1476 | - if detect_progress_indicator(): |
1477 | - progress_indicator = Popen(detect_progress_indicator(), |
1478 | - stdin=PIPE) |
1479 | - for iteration in range(1, iterations+1): |
1480 | - timestamp = int(time()) |
1481 | - start_marker = 'CHECKBOX SLEEP TEST START %s' % timestamp |
1482 | - end_marker = 'CHECKBOX SLEEP TEST STOP %s' % timestamp |
1483 | - with open(args.log + '.log', 'a') as f: |
1484 | - f.write('{:=^80}\n'.format(' Iteration {} '.format(iteration))) |
1485 | - syslog(LOG_INFO, '---' + start_marker + '---' + str(time())) |
1486 | - command = ('fwts -q --stdout-summary -r %s %s' |
1487 | - % (args.log, ' '.join(tests))) |
1488 | - results['sleep'] = (Popen(command, stdout=PIPE, shell=True) |
1489 | - .communicate()[0].strip()).decode() |
1490 | - syslog(LOG_INFO, '---' + end_marker + '---' + str(time())) |
1491 | - sleep(2) |
1492 | - if 's4' not in args.sleep: |
1493 | - sleep_times = get_sleep_times(start_marker, |
1494 | - end_marker, |
1495 | - args.sleep_time, |
1496 | - args.resume_time) |
1497 | - iteration_results[iteration] = sleep_times |
1498 | - progress_tuple = (iteration, |
1499 | - iteration_results[iteration][0], |
1500 | - iteration_results[iteration][1], |
1501 | - iteration_results[iteration][2]) |
1502 | - progress_string = (' - Cycle %s: Status: %s ' |
1503 | - 'Sleep Elapsed: %0.5f ' |
1504 | - 'Resume Elapsed: ' |
1505 | - ' %0.5f' % progress_tuple) |
1506 | - progress_pct = "{}".format(int(100 * iteration / iterations)) |
1507 | - if "zenity" in detect_progress_indicator(): |
1508 | - progress_indicator.stdin.write("# {}\n".format( |
1509 | - progress_string).encode('utf-8')) |
1510 | - progress_indicator.stdin.write("{}\n".format( |
1511 | - progress_pct).encode('utf-8')) |
1512 | - progress_indicator.stdin.flush() |
1513 | - elif "dialog" in detect_progress_indicator(): |
1514 | - progress_indicator.stdin.write("XXX\n".encode('utf-8')) |
1515 | - progress_indicator.stdin.write( |
1516 | - progress_pct.encode('utf-8')) |
1517 | - progress_indicator.stdin.write( |
1518 | - "\nTest progress\n".encode('utf-8')) |
1519 | - progress_indicator.stdin.write( |
1520 | - progress_string.encode('utf-8')) |
1521 | - progress_indicator.stdin.write( |
1522 | - "\nXXX\n".encode('utf-8')) |
1523 | - progress_indicator.stdin.flush() |
1524 | - else: |
1525 | - print(progress_string, flush=True) |
1526 | - if detect_progress_indicator(): |
1527 | - progress_indicator.terminate() |
1528 | - |
1529 | - if 's4' not in args.sleep: |
1530 | - average_times(iteration_results) |
1531 | - for run in iteration_results.keys(): |
1532 | - if 'FAIL' in iteration_results[run]: |
1533 | - results['sleep'] = 'FAILED_CRITICAL' |
1534 | - else: |
1535 | - for test in tests: |
1536 | - # ACPI tests can now be run with --acpitests (fwts >= 15.07.00) |
1537 | - log = args.log |
1538 | - # Split the log file for HWE (only if -t is not used) |
1539 | - if test == 'acpitests': |
1540 | - test = '--acpitests' |
1541 | - command = ('fwts -q --stdout-summary -r %s %s' |
1542 | - % (log, test)) |
1543 | - results[test] = (Popen(command, stdout=PIPE, shell=True) |
1544 | - .communicate()[0].strip()).decode() |
1545 | - |
1546 | - # parse the summaries |
1547 | - for test in results.keys(): |
1548 | - if 'FAILED_CRITICAL' in results[test]: |
1549 | - critical_fails.append(test) |
1550 | - if 'FAILED_HIGH' in results[test]: |
1551 | - high_fails.append(test) |
1552 | - if 'FAILED_MEDIUM' in results[test]: |
1553 | - medium_fails.append(test) |
1554 | - if 'FAILED_LOW' in results[test]: |
1555 | - low_fails.append(test) |
1556 | - if 'PASSED' in results[test]: |
1557 | - passed.append(test) |
1558 | - if 'ABORTED' in results[test]: |
1559 | - aborted.append(test) |
1560 | - else: |
1561 | - continue |
1562 | - |
1563 | - if critical_fails: |
1564 | - print("Critical Failures: %d" % len(critical_fails)) |
1565 | - print("WARNING: The following test cases were reported as critical\n" |
1566 | - "level failures by fwts. Please review the log at\n" |
1567 | - "%s for more information." % args.log) |
1568 | - for test in critical_fails: |
1569 | - print(" - " + test) |
1570 | - if high_fails: |
1571 | - print("High Failures: %d" % len(high_fails)) |
1572 | - print("WARNING: The following test cases were reported as high\n" |
1573 | - "level failures by fwts. Please review the log at\n" |
1574 | - "%s for more information." % args.log) |
1575 | - for test in high_fails: |
1576 | - print(" - " + test) |
1577 | - if medium_fails: |
1578 | - print("Medium Failures: %d" % len(medium_fails)) |
1579 | - print("WARNING: The following test cases were reported as medium\n" |
1580 | - "level failures by fwts. Please review the log at\n" |
1581 | - "%s for more information." % args.log) |
1582 | - for test in medium_fails: |
1583 | - print(" - " + test) |
1584 | - if low_fails: |
1585 | - print("Low Failures: %d" % len(low_fails)) |
1586 | - print("WARNING: The following test cases were reported as low\n" |
1587 | - "level failures by fwts. Please review the log at\n" |
1588 | - "%s for more information." % args.log) |
1589 | - for test in low_fails: |
1590 | - print(" - " + test) |
1591 | - if passed: |
1592 | - print("Passed: %d" % len(passed)) |
1593 | - for test in passed: |
1594 | - print(" - " + test) |
1595 | - if aborted: |
1596 | - print("Aborted Tests: %d" % len(aborted)) |
1597 | - print("WARNING: The following test cases were aborted by fwts\n" |
1598 | - "Please review the log at %s for more information." |
1599 | - % args.log) |
1600 | - for test in aborted: |
1601 | - print(" - " + test) |
1602 | - |
1603 | - if args.fail_level is not 'none': |
1604 | - if fail_priority == fail_levels['FAILED_CRITICAL']: |
1605 | - if critical_fails: |
1606 | - return 1 |
1607 | - if fail_priority == fail_levels['FAILED_HIGH']: |
1608 | - if critical_fails or high_fails: |
1609 | - return 1 |
1610 | - if fail_priority == fail_levels['FAILED_MEDIUM']: |
1611 | - if critical_fails or high_fails or medium_fails: |
1612 | - return 1 |
1613 | - if fail_priority == fail_levels['FAILED_LOW']: |
1614 | - if critical_fails or high_fails or medium_fails or low_fails: |
1615 | - return 1 |
1616 | - if fail_priority == fail_levels['FAILED_ABORTED']: |
1617 | - if aborted or critical_fails or high_fails: |
1618 | - return 1 |
1619 | - |
1620 | - return 0 |
1621 | - |
1622 | -if __name__ == '__main__': |
1623 | - sys.exit(main()) |
1624 | diff --git a/bin/gateway_ping_test b/bin/gateway_ping_test |
1625 | deleted file mode 100755 |
1626 | index 14c5d8e..0000000 |
1627 | --- a/bin/gateway_ping_test |
1628 | +++ /dev/null |
1629 | @@ -1,286 +0,0 @@ |
1630 | -#!/usr/bin/python3 |
1631 | -# This file is part of Checkbox. |
1632 | -# |
1633 | -# Copyright 2007-2014 Canonical Ltd. |
1634 | -# Written by: |
1635 | -# Brendan Donegan <brendan.donegan@canonical.com> |
1636 | -# Daniel Manrique <daniel.manrique@canonical.com> |
1637 | -# David Murphy <david.murphy@canonical.com> |
1638 | -# Javier Collado <javier.collado@canonical.com> |
1639 | -# Jeff Lane <jeffrey.lane@canonical.com> |
1640 | -# Marc Tardif <marc.tardif@canonical.com> |
1641 | -# Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com> |
1642 | -# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1643 | -# |
1644 | -# Checkbox is free software: you can redistribute it and/or modify |
1645 | -# it under the terms of the GNU General Public License version 3, |
1646 | -# as published by the Free Software Foundation. |
1647 | -# |
1648 | -# Checkbox is distributed in the hope that it will be useful, |
1649 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1650 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1651 | -# GNU General Public License for more details. |
1652 | -# |
1653 | -# You should have received a copy of the GNU General Public License |
1654 | -# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1655 | - |
1656 | -from gettext import gettext as _ |
1657 | -import argparse |
1658 | -import errno |
1659 | -import gettext |
1660 | -import logging |
1661 | -import os |
1662 | -import re |
1663 | -import socket |
1664 | -import struct |
1665 | -import subprocess |
1666 | -import sys |
1667 | -import time |
1668 | - |
1669 | - |
1670 | -class Route: |
1671 | - """ |
1672 | - Gets routing information from the system. |
1673 | - """ |
1674 | - |
1675 | - def _num_to_dotted_quad(self, number): |
1676 | - """ |
1677 | - Convert long int to dotted quad string |
1678 | - """ |
1679 | - return socket.inet_ntoa(struct.pack("<L", number)) |
1680 | - |
1681 | - def _get_default_gateway_from_proc(self): |
1682 | - """ |
1683 | - Returns the current default gateway, reading that from /proc |
1684 | - """ |
1685 | - logging.debug(_("Reading default gateway information from /proc")) |
1686 | - try: |
1687 | - with open("/proc/net/route", "rt") as stream: |
1688 | - route = stream.read() |
1689 | - except: |
1690 | - logging.error(_("Failed to read def gateway from /proc")) |
1691 | - return None |
1692 | - else: |
1693 | - h = re.compile("\n(?P<interface>\w+)\s+00000000\s+" |
1694 | - "(?P<def_gateway>[\w]+)\s+") |
1695 | - w = h.search(route) |
1696 | - if w: |
1697 | - if w.group("def_gateway"): |
1698 | - return self._num_to_dotted_quad( |
1699 | - int(w.group("def_gateway"), 16)) |
1700 | - else: |
1701 | - logging.error( |
1702 | - _("Could not find def gateway info in /proc")) |
1703 | - return None |
1704 | - else: |
1705 | - logging.error(_("Could not find def gateway info in /proc")) |
1706 | - return None |
1707 | - |
1708 | - def _get_default_gateway_from_bin_route(self): |
1709 | - """ |
1710 | - Get default gateway from /sbin/route -n |
1711 | - Called by get_default_gateway |
1712 | - and is only used if could not get that from /proc |
1713 | - """ |
1714 | - logging.debug( |
1715 | - _("Reading default gateway information from route binary")) |
1716 | - routebin = subprocess.getstatusoutput( |
1717 | - "export LANGUAGE=C; " "/usr/bin/env route -n") |
1718 | - if routebin[0] == 0: |
1719 | - h = re.compile("\n0.0.0.0\s+(?P<def_gateway>[\w.]+)\s+") |
1720 | - w = h.search(routebin[1]) |
1721 | - if w: |
1722 | - def_gateway = w.group("def_gateway") |
1723 | - if def_gateway: |
1724 | - return def_gateway |
1725 | - logging.error(_("Could not find default gateway by running route")) |
1726 | - return None |
1727 | - |
1728 | - def get_hostname(self): |
1729 | - return socket.gethostname() |
1730 | - |
1731 | - def get_default_gateway(self): |
1732 | - t1 = self._get_default_gateway_from_proc() |
1733 | - if not t1: |
1734 | - t1 = self._get_default_gateway_from_bin_route() |
1735 | - return t1 |
1736 | - |
1737 | - |
1738 | -def get_host_to_ping(interface=None, verbose=False, default=None): |
1739 | - # Get list of all IPs from all my interfaces, |
1740 | - interface_list = subprocess.check_output(["ip", "-o", 'addr', 'show']) |
1741 | - reg = re.compile('\d: (?P<iface>\w+) +inet (?P<address>[\d\.]+)/' |
1742 | - '(?P<netmask>[\d]+) brd (?P<broadcast>[\d\.]+)') |
1743 | - # Will magically exclude lo because it lacks brd field |
1744 | - interfaces = reg.findall(interface_list.decode()) |
1745 | - # ping -b the network on each one (one ping only) |
1746 | - # exclude the ones not specified in iface |
1747 | - for iface in interfaces: |
1748 | - if not interface or iface[0] == interface: |
1749 | - # Use check_output even if I'll discard the output |
1750 | - # looks cleaner than using .call and redirecting stdout to null |
1751 | - try: |
1752 | - subprocess.check_output(["ping", "-q", "-c", "1", "-b", |
1753 | - iface[3]], stderr=subprocess.STDOUT) |
1754 | - except subprocess.CalledProcessError: |
1755 | - pass |
1756 | - # If default host given, ping it as well, |
1757 | - # to try to get it into the arp table. |
1758 | - # Needed in case it's not responding to broadcasts. |
1759 | - if default: |
1760 | - try: |
1761 | - subprocess.check_output(["ping", "-q", "-c", "1", default], |
1762 | - stderr=subprocess.STDOUT) |
1763 | - except subprocess.CalledProcessError: |
1764 | - pass |
1765 | - ARP_POPULATE_TRIES = 10 |
1766 | - num_tries = 0 |
1767 | - while num_tries < ARP_POPULATE_TRIES: |
1768 | - # Get output from arp -a -n to get known IPs |
1769 | - known_ips = subprocess.check_output(["arp", "-a", "-n"]) |
1770 | - reg = re.compile('\? \((?P<ip>[\d.]+)\) at (?P<mac>[a-f0-9\:]+) ' |
1771 | - '\[ether\] on (?P<iface>[\w\d]+)') |
1772 | - # Filter (if needed) IPs not on the specified interface |
1773 | - pingable_ips = [pingable[0] for pingable in reg.findall( |
1774 | - known_ips.decode()) if not interface |
1775 | - or pingable[2] == interface] |
1776 | - # If the default given ip is among the remaining ones, |
1777 | - # ping that. |
1778 | - if default and default in pingable_ips: |
1779 | - if verbose: |
1780 | - print(_( |
1781 | - "Desired ip address {0} is reachable, using it" |
1782 | - ).format(default)) |
1783 | - return default |
1784 | - # If not, choose another IP. |
1785 | - address_to_ping = pingable_ips[0] if len(pingable_ips) else None |
1786 | - if verbose: |
1787 | - print(_( |
1788 | - "Desired ip address {0} is not reachable from {1}," |
1789 | - " using {2} instead" |
1790 | - ).format(default, interface, address_to_ping)) |
1791 | - if address_to_ping: |
1792 | - return address_to_ping |
1793 | - time.sleep(2) |
1794 | - num_tries += 1 |
1795 | - # Wait time expired |
1796 | - return None |
1797 | - |
1798 | - |
1799 | -def ping(host, interface, count, deadline, verbose=False): |
1800 | - command = ["ping", str(host), "-c", str(count), "-w", str(deadline)] |
1801 | - if interface: |
1802 | - command.append("-I{}".format(interface)) |
1803 | - reg = re.compile( |
1804 | - r"(\d+) packets transmitted, (\d+) received, (\d+)% packet loss") |
1805 | - ping_summary = {'transmitted': 0, 'received': 0, 'pct_loss': 0} |
1806 | - try: |
1807 | - output = subprocess.check_output(command, universal_newlines=True) |
1808 | - except OSError as exc: |
1809 | - if exc.errno == errno.ENOENT: |
1810 | - # No ping command present; |
1811 | - # default exception message is informative enough. |
1812 | - print(exc) |
1813 | - else: |
1814 | - raise |
1815 | - except subprocess.CalledProcessError as excp: |
1816 | - # Ping returned fail exit code |
1817 | - print(_("ERROR: ping result: {0}").format(excp)) |
1818 | - else: |
1819 | - if verbose: |
1820 | - print(output) |
1821 | - received = re.findall(reg, output) |
1822 | - if received: |
1823 | - ping_summary = received[0] |
1824 | - ping_summary = { |
1825 | - 'transmitted': int(ping_summary[0]), |
1826 | - 'received': int(ping_summary[1]), |
1827 | - 'pct_loss': int(ping_summary[2])} |
1828 | - return ping_summary |
1829 | - |
1830 | - |
1831 | -def main(args): |
1832 | - gettext.textdomain("2015.com.canonical.qa.plano") |
1833 | - gettext.bindtextdomain("2015.com.canonical.qa.plano", |
1834 | - os.getenv("CHECKBOX_PROVIDER_LOCALE_DIR", None)) |
1835 | - default_count = 2 |
1836 | - default_delay = 4 |
1837 | - route = Route() |
1838 | - parser = argparse.ArgumentParser() |
1839 | - parser.add_argument( |
1840 | - "host", nargs='?', default=route.get_default_gateway(), |
1841 | - help=_("host to ping")) |
1842 | - parser.add_argument( |
1843 | - "-c", "--count", default=default_count, type=int, |
1844 | - help=_("number of packets to send")) |
1845 | - parser.add_argument( |
1846 | - "-d", "--deadline", default=default_delay, type=int, |
1847 | - help=_("timeout in seconds")) |
1848 | - parser.add_argument( |
1849 | - "-t", "--threshold", default=0, type=int, |
1850 | - help=_("allowed packet loss percentage (default: %(default)s)")) |
1851 | - parser.add_argument( |
1852 | - "-v", "--verbose", action='store_true', help=_("be verbose")) |
1853 | - parser.add_argument( |
1854 | - "-I", "--interface", help=_("use specified interface to send packets")) |
1855 | - args = parser.parse_args() |
1856 | - # Ensure count and deadline make sense. Adjust them if not. |
1857 | - if args.deadline != default_delay and args.count != default_count: |
1858 | - # Ensure they're both consistent, and exit with a warning if not, |
1859 | - # rather than modifying what the user explicitly set. |
1860 | - if args.deadline <= args.count: |
1861 | - # FIXME: this cannot ever be translated correctly |
1862 | - print(_( |
1863 | - "ERROR: not enough time for {0} pings in {1} seconds" |
1864 | - ).format(args.count, args.deadline)) |
1865 | - return 1 |
1866 | - elif args.deadline != default_delay: |
1867 | - # Adjust count according to delay. |
1868 | - args.count = args.deadline - 1 |
1869 | - if args.count < 1: |
1870 | - args.count = 1 |
1871 | - if args.verbose: |
1872 | - # FIXME: this cannot ever be translated correctly |
1873 | - print(_( |
1874 | - "Adjusting ping count to {0} to fit in {1}-second deadline" |
1875 | - ).format(args.count, args.deadline)) |
1876 | - else: |
1877 | - # Adjust delay according to count |
1878 | - args.deadline = args.count + 1 |
1879 | - if args.verbose: |
1880 | - # FIXME: this cannot ever be translated correctly |
1881 | - print(_( |
1882 | - "Adjusting deadline to {0} seconds to fit {1} pings" |
1883 | - ).format(args.deadline, args.count)) |
1884 | - # If given host is not pingable, override with something pingable. |
1885 | - host = get_host_to_ping( |
1886 | - interface=args.interface, verbose=args.verbose, default=args.host) |
1887 | - if args.verbose: |
1888 | - print(_("Checking connectivity to {0}").format(host)) |
1889 | - ping_summary = None |
1890 | - if host: |
1891 | - ping_summary = ping(host, args.interface, args.count, |
1892 | - args.deadline, args.verbose) |
1893 | - if ping_summary is None or ping_summary['received'] == 0: |
1894 | - print(_("No Internet connection")) |
1895 | - return 1 |
1896 | - elif ping_summary['transmitted'] != ping_summary['received']: |
1897 | - print(_("Connection established, but lost {0}% of packets").format( |
1898 | - ping_summary['pct_loss'])) |
1899 | - if ping_summary['pct_loss'] > args.threshold: |
1900 | - print(_( |
1901 | - "FAIL: {0}% packet loss is higher than {1}% threshold" |
1902 | - ).format(ping_summary['pct_loss'], args.threshold)) |
1903 | - return 1 |
1904 | - else: |
1905 | - print(_( |
1906 | - "PASS: {0}% packet loss is within {1}% threshold" |
1907 | - ).format(ping_summary['pct_loss'], args.threshold)) |
1908 | - return 0 |
1909 | - else: |
1910 | - print(_("Connection to test host fully established")) |
1911 | - return 0 |
1912 | - |
1913 | - |
1914 | -if __name__ == "__main__": |
1915 | - sys.exit(main(sys.argv[1:])) |
1916 | diff --git a/bin/lsmod_info b/bin/lsmod_info |
1917 | deleted file mode 100755 |
1918 | index 3b81542..0000000 |
1919 | --- a/bin/lsmod_info |
1920 | +++ /dev/null |
1921 | @@ -1,40 +0,0 @@ |
1922 | -#!/usr/bin/env python3 |
1923 | -import sys |
1924 | -from checkbox_support.parsers.modinfo import ModinfoParser |
1925 | -from subprocess import Popen, PIPE, check_output, CalledProcessError |
1926 | - |
1927 | - |
1928 | -def main(): |
1929 | - process = Popen('lsmod', stdout=PIPE, stderr=PIPE, universal_newlines=True) |
1930 | - data = process.stdout.readlines() |
1931 | - # Delete the first item because it's headers from lsmod output |
1932 | - data.pop(0) |
1933 | - module_list = [module.split()[0].strip() for module in data] |
1934 | - |
1935 | - cmd = '/sbin/modinfo' |
1936 | - for module in sorted(module_list): |
1937 | - version = '' |
1938 | - stream = b'' |
1939 | - try: |
1940 | - stream = check_output([cmd, module], |
1941 | - stderr=PIPE, |
1942 | - universal_newlines=False) |
1943 | - except CalledProcessError as e: |
1944 | - if e.returncode != 1: |
1945 | - raise e |
1946 | - else: |
1947 | - version = 'Unavailable' |
1948 | - |
1949 | - stream = stream.decode('utf-8') |
1950 | - |
1951 | - parser = ModinfoParser(stream) |
1952 | - |
1953 | - if not version: |
1954 | - version = parser.get_field('version') |
1955 | - if not version: |
1956 | - version = parser.get_field('vermagic').split()[0] |
1957 | - print('%s: %s' % (module, version)) |
1958 | - return 0 |
1959 | - |
1960 | -if __name__ == '__main__': |
1961 | - sys.exit(main()) |
1962 | diff --git a/bin/memory_compare b/bin/memory_compare |
1963 | deleted file mode 100755 |
1964 | index 02e2def..0000000 |
1965 | --- a/bin/memory_compare |
1966 | +++ /dev/null |
1967 | @@ -1,138 +0,0 @@ |
1968 | -#!/usr/bin/env python3 |
1969 | -# -*- coding: utf-8 -*- |
1970 | -# |
1971 | -# This file is part of Checkbox. |
1972 | -# |
1973 | -# Copyright 2014 Canonical Ltd. |
1974 | -# |
1975 | -# Authors: |
1976 | -# Brendan Donegan <brendan.donegan@canonical.com> |
1977 | -# Jeff Lane <jeff.lane@canonical.com> |
1978 | -# Sylvain Pineau <sylvain.pineau@canonical.com> |
1979 | -# |
1980 | -# Checkbox is free software: you can redistribute it and/or modify |
1981 | -# it under the terms of the GNU General Public License version 3, |
1982 | -# as published by the Free Software Foundation. |
1983 | -# |
1984 | -# Checkbox is distributed in the hope that it will be useful, |
1985 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1986 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1987 | -# GNU General Public License for more details. |
1988 | -# |
1989 | -# You should have received a copy of the GNU General Public License |
1990 | -# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1991 | - |
1992 | -import os |
1993 | -import sys |
1994 | -import re |
1995 | -from math import log, copysign |
1996 | -from subprocess import check_output, CalledProcessError, PIPE |
1997 | - |
1998 | -from checkbox_support.helpers.human_readable_bytes import HumanReadableBytes |
1999 | -from checkbox_support.parsers.lshwjson import LshwJsonParser |
2000 | -from checkbox_support.parsers.meminfo import MeminfoParser |
2001 | - |
2002 | - |
2003 | -class LshwJsonResult: |
2004 | - |
2005 | - memory_reported = 0 |
2006 | - banks_reported = 0 |
2007 | - |
2008 | - # jlane LP:1525009 |
2009 | - # Discovered the case can change, my x86 systems used "System Memory" |
2010 | - # Failing ARM system used "System memory" |
2011 | - desc_regex = re.compile('System Memory', re.IGNORECASE) |
2012 | - |
2013 | - def addHardware(self, hardware): |
2014 | - if self.desc_regex.match(str(hardware.get('description',0))): |
2015 | - self.memory_reported += int(hardware.get('size', 0)) |
2016 | - elif 'bank' in hardware['id']: |
2017 | - self.banks_reported += int(hardware.get('size', 0)) |
2018 | - |
2019 | - |
2020 | -def get_installed_memory_size(): |
2021 | - try: |
2022 | - output = check_output(['lshw', '-json'], |
2023 | - universal_newlines=True, |
2024 | - stderr=PIPE) |
2025 | - except CalledProcessError: |
2026 | - return 0 |
2027 | - lshw = LshwJsonParser(output) |
2028 | - result = LshwJsonResult() |
2029 | - lshw.run(result) |
2030 | - |
2031 | - if result.memory_reported: |
2032 | - return result.memory_reported |
2033 | - else: |
2034 | - return result.banks_reported |
2035 | - |
2036 | - |
2037 | -class MeminfoResult: |
2038 | - |
2039 | - memtotal = 0 |
2040 | - |
2041 | - def setMemory(self, memory): |
2042 | - self.memtotal = memory['total'] |
2043 | - |
2044 | - |
2045 | -def get_visible_memory_size(): |
2046 | - parser = MeminfoParser(open('/proc/meminfo')) |
2047 | - result = MeminfoResult() |
2048 | - parser.run(result) |
2049 | - |
2050 | - return result.memtotal |
2051 | - |
2052 | - |
2053 | -def get_threshold(installed_memory): |
2054 | - GB = 1024**3 |
2055 | - if installed_memory <= 2 * GB: |
2056 | - return 25 |
2057 | - elif installed_memory <= 6 * GB: |
2058 | - return 20 |
2059 | - else: |
2060 | - return 10 |
2061 | - |
2062 | - |
2063 | -def main(): |
2064 | - if os.geteuid() != 0: |
2065 | - print("This script must be run as root.", file=sys.stderr) |
2066 | - return 1 |
2067 | - |
2068 | - installed_memory = HumanReadableBytes(get_installed_memory_size()) |
2069 | - visible_memory = HumanReadableBytes(get_visible_memory_size()) |
2070 | - threshold = get_threshold(installed_memory) |
2071 | - |
2072 | - difference = HumanReadableBytes(installed_memory - visible_memory) |
2073 | - try: |
2074 | - percentage = difference / installed_memory * 100 |
2075 | - except ZeroDivisionError: |
2076 | - print("Results:") |
2077 | - print("\t/proc/meminfo reports:\t{}".format(visible_memory), |
2078 | - file=sys.stderr) |
2079 | - print("\tlshw reports:\t{}".format(installed_memory), |
2080 | - file=sys.stderr) |
2081 | - print("\nFAIL: Either lshw or /proc/meminfo returned a memory size " |
2082 | - "of 0 kB", file=sys.stderr) |
2083 | - return 1 |
2084 | - |
2085 | - if percentage <= threshold: |
2086 | - print("Results:") |
2087 | - print("\t/proc/meminfo reports:\t{}".format(visible_memory)) |
2088 | - print("\tlshw reports:\t{}".format(installed_memory)) |
2089 | - print("\nPASS: Meminfo reports %s less than lshw, a " |
2090 | - "difference of %.2f%%. This is less than the " |
2091 | - "%d%% variance allowed." % (difference, percentage, threshold)) |
2092 | - return 0 |
2093 | - else: |
2094 | - print("Results:", file=sys.stderr) |
2095 | - print("\t/proc/meminfo reports:\t{}".format(visible_memory), |
2096 | - file=sys.stderr) |
2097 | - print("\tlshw reports:\t{}".format(installed_memory), file=sys.stderr) |
2098 | - print("\nFAIL: Meminfo reports %d less than lshw, " |
2099 | - "a difference of %.2f%%. Only a variance of %d%% in " |
2100 | - "reported memory is allowed." % |
2101 | - (difference, percentage, threshold), file=sys.stderr) |
2102 | - return 1 |
2103 | - |
2104 | -if __name__ == "__main__": |
2105 | - sys.exit(main()) |
2106 | diff --git a/bin/memory_test b/bin/memory_test |
2107 | deleted file mode 100755 |
2108 | index 4cd6008..0000000 |
2109 | --- a/bin/memory_test |
2110 | +++ /dev/null |
2111 | @@ -1,239 +0,0 @@ |
2112 | -#!/usr/bin/env python3 |
2113 | - |
2114 | -import os |
2115 | -import sys |
2116 | -import re |
2117 | - |
2118 | -from argparse import ArgumentParser |
2119 | -from subprocess import Popen, PIPE |
2120 | - |
2121 | - |
2122 | -class MemoryTest(): |
2123 | - |
2124 | - def __init__(self): |
2125 | - self.free_memory = 0 |
2126 | - self.system_memory = 0 |
2127 | - self.swap_memory = 0 |
2128 | - self.process_memory = 0 |
2129 | - self.is_process_limited = False |
2130 | - |
2131 | - @property |
2132 | - def threaded_memtest_script(self): |
2133 | - directory = os.path.dirname(os.path.abspath(__file__)) |
2134 | - return os.path.join(directory, "threaded_memtest") |
2135 | - |
2136 | - def _get_memory(self): |
2137 | - mem_info = open("/proc/meminfo", "r") |
2138 | - try: |
2139 | - while True: |
2140 | - line = mem_info.readline() |
2141 | - if line: |
2142 | - tokens = line.split() |
2143 | - if len(tokens) == 3: |
2144 | - if "MemTotal:" == tokens[0].strip(): |
2145 | - self.system_memory = \ |
2146 | - int(tokens[1].strip()) // 1024 |
2147 | - elif tokens[0].strip() in ["MemFree:", |
2148 | - "Cached:", |
2149 | - "Buffers:"]: |
2150 | - self.free_memory += \ |
2151 | - int(tokens[1].strip()) // 1024 |
2152 | - elif "SwapTotal:" == tokens[0].strip(): |
2153 | - self.swap_memory = \ |
2154 | - int(tokens[1].strip()) // 1024 |
2155 | - else: |
2156 | - break |
2157 | - except Exception as e: |
2158 | - print("ERROR: Unable to get data from /proc/meminfo", |
2159 | - file=sys.stderr) |
2160 | - print(e, file=sys.stderr) |
2161 | - finally: |
2162 | - mem_info.close() |
2163 | - |
2164 | - def _command(self, command, shell=True): |
2165 | - proc = Popen(command, |
2166 | - shell=shell, |
2167 | - stdout=PIPE, |
2168 | - stderr=PIPE) |
2169 | - return proc |
2170 | - |
2171 | - def _command_out(self, command, shell=True): |
2172 | - proc = self._command(command, shell) |
2173 | - return proc.communicate()[0].strip() |
2174 | - |
2175 | - def get_limits(self): |
2176 | - self._get_memory() |
2177 | - print("System Memory: %u MB" % self.system_memory) |
2178 | - print("Free Memory: %u MB" % self.free_memory) |
2179 | - print("Swap Memory: %u MB" % self.swap_memory) |
2180 | - |
2181 | - if self.system_memory == 0: |
2182 | - print("ERROR: could not determine system RAM", file=sys.stderr) |
2183 | - return False |
2184 | - |
2185 | - # Process Memory |
2186 | - self.process_memory = self.free_memory |
2187 | - try: |
2188 | - arch = self._command_out("arch").decode() |
2189 | - if (re.match(r"(i[0-9]86|s390|arm.*)", arch) |
2190 | - and self.free_memory > 1024): |
2191 | - self.is_process_limited = True |
2192 | - self.process_memory = 1024 # MB, due to 32-bit address space |
2193 | - print("%s arch, Limiting Process Memory: %u" % ( |
2194 | - arch, self.process_memory)) |
2195 | - # others? what about PAE kernel? |
2196 | - except Exception as e: |
2197 | - print("ERROR: could not determine system architecture via arch", |
2198 | - file=sys.stderr) |
2199 | - print(e, file=sys.stderr) |
2200 | - return False |
2201 | - return True |
2202 | - |
2203 | - def run(self): |
2204 | - PASSED = 0 |
2205 | - FAILED = 1 |
2206 | - |
2207 | - limits = self.get_limits() |
2208 | - if not limits: |
2209 | - return FAILED |
2210 | - |
2211 | - # if process memory is limited, run multiple processes |
2212 | - if self.is_process_limited: |
2213 | - print("Running Multiple Process Memory Test") |
2214 | - if not self.run_multiple_process_test(): |
2215 | - return FAILED |
2216 | - else: |
2217 | - print("Running Single Process Memory Test") |
2218 | - if not self.run_single_process_test(): |
2219 | - return FAILED |
2220 | - |
2221 | - # otherwised, passed |
2222 | - return PASSED |
2223 | - |
2224 | - def run_single_process_test(self): |
2225 | - if not self.run_threaded_memory_test(): |
2226 | - return False |
2227 | - return True |
2228 | - |
2229 | - def run_multiple_process_test(self): |
2230 | - processes = self.free_memory // self.process_memory |
2231 | - # if not swap-less, add a process to hit swap |
2232 | - if not self.swap_memory == 0: |
2233 | - processes += 1 |
2234 | - # check to make sure there's enough swap |
2235 | - required_memory = self.process_memory * processes |
2236 | - if required_memory > self.system_memory + self.swap_memory: |
2237 | - print("ERROR: this test requires a minimum of %u KB of swap " |
2238 | - "memory (%u configured)" % ( |
2239 | - required_memory - self.system_memory, |
2240 | - self.swap_memory), file=sys.stderr) |
2241 | - print("Testing memory with %u processes" % processes) |
2242 | - |
2243 | - print("Running threaded memory test:") |
2244 | - run_time = 60 # sec. |
2245 | - if not self.run_processes(processes, "%s -qv -m%um -t%u" % ( |
2246 | - self.threaded_memtest_script, self.process_memory, run_time)): |
2247 | - print("Multi-process, threaded memory Test FAILED", |
2248 | - file=sys.stderr) |
2249 | - return False |
2250 | - |
2251 | - return True |
2252 | - |
2253 | - def run_threaded_memory_test(self): |
2254 | - # single-process threaded test |
2255 | - print("Starting Threaded Memory Test") |
2256 | - |
2257 | - # run for Free Memory plus the lessor of 5% or 1GB |
2258 | - memory = (self.free_memory * 5) / 100 |
2259 | - if memory > 1024: # MB |
2260 | - memory = 1024 # MB |
2261 | - memory = memory + self.free_memory |
2262 | - print("Running for %d MB total memory" % memory) |
2263 | - |
2264 | - # run a test that will swap |
2265 | - if not self.swap_memory == 0: |
2266 | - |
2267 | - # is there enough swap memory for the test? |
2268 | - if memory > self.system_memory + self.swap_memory: |
2269 | - print("ERROR: this test requires a minimum of %u KB of swap " |
2270 | - "memory (%u configured)" |
2271 | - % (memory - self.system_memory, self.swap_memory), |
2272 | - file=sys.stderr) |
2273 | - return False |
2274 | - |
2275 | - # otherwise |
2276 | - run_time = 60 # sec. |
2277 | - print("Running for more than free memory at %u MB for %u sec." % ( |
2278 | - memory, run_time)) |
2279 | - |
2280 | - command = "%s -qv -m%um -t%u" % ( |
2281 | - self.threaded_memtest_script, memory, run_time) |
2282 | - print("Command is: %s" % command) |
2283 | - process = self._command(command) |
2284 | - process.communicate() |
2285 | - if process.returncode != 0: |
2286 | - print("%s returned code %s" % (self.threaded_memtest_script, |
2287 | - process.returncode), file=sys.stderr) |
2288 | - print("More Than Free Memory Test failed", file=sys.stderr) |
2289 | - return False |
2290 | - print("More than free memory test complete.") |
2291 | - |
2292 | - # run again for 15 minutes |
2293 | - print("Running for free memory") |
2294 | - process = self._command("%s -qv" % self.threaded_memtest_script) |
2295 | - process.communicate() |
2296 | - if process.returncode != 0: |
2297 | - print("Free Memory Test failed", file=sys.stderr) |
2298 | - else: |
2299 | - print("Free Memory Test succeeded") |
2300 | - sys.stdout.flush() |
2301 | - return (process.returncode == 0) |
2302 | - |
2303 | - def run_processes(self, number, command): |
2304 | - passed = True |
2305 | - pipe = [] |
2306 | - for i in range(number): |
2307 | - pipe.append(self._command(command)) |
2308 | - print("Started: process %u pid %u: %s" % (i, pipe[i].pid, command)) |
2309 | - sys.stdout.flush() |
2310 | - waiting = True |
2311 | - while waiting: |
2312 | - waiting = False |
2313 | - for i in range(number): |
2314 | - if pipe[i]: |
2315 | - line = pipe[i].communicate()[0] |
2316 | - if line and len(line) > 1: |
2317 | - print("process %u pid %u: %s" % (i, pipe[i].pid, line)) |
2318 | - sys.stdout.flush() |
2319 | - if pipe[i].poll() == -1: |
2320 | - waiting = True |
2321 | - else: |
2322 | - return_value = pipe[i].poll() |
2323 | - if return_value != 0: |
2324 | - print("ERROR: process %u pid %u retuned %u" |
2325 | - % (i, pipe[i].pid, return_value), |
2326 | - file=sys.stderr) |
2327 | - passed = False |
2328 | - print("process %u pid %u returned success" |
2329 | - % (i, pipe[i].pid)) |
2330 | - pipe[i] = None |
2331 | - sys.stdout.flush() |
2332 | - return passed |
2333 | - |
2334 | - |
2335 | -def main(args): |
2336 | - parser = ArgumentParser() |
2337 | - parser.add_argument("-q", "--quiet", action="store_true", |
2338 | - help="Suppress output.") |
2339 | - args = parser.parse_args(args) |
2340 | - |
2341 | - if args.quiet: |
2342 | - sys.stdout = open(os.devnull, 'a') |
2343 | - sys.stderr = open(os.devnull, 'a') |
2344 | - |
2345 | - test = MemoryTest() |
2346 | - return test.run() |
2347 | - |
2348 | - |
2349 | -if __name__ == "__main__": |
2350 | - sys.exit(main(sys.argv[1:])) |
2351 | diff --git a/bin/network_device_info b/bin/network_device_info |
2352 | deleted file mode 100755 |
2353 | index 0e7479e..0000000 |
2354 | --- a/bin/network_device_info |
2355 | +++ /dev/null |
2356 | @@ -1,266 +0,0 @@ |
2357 | -#!/usr/bin/env python3 |
2358 | -# |
2359 | -# This program is free software: you can redistribute it and/or modify |
2360 | -# it under the terms of the GNU General Public License version 3, |
2361 | -# as published by the Free Software Foundation. |
2362 | -# |
2363 | -# This program is distributed in the hope that it will be useful, |
2364 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2365 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2366 | -# GNU General Public License for more details. |
2367 | -# |
2368 | -# You should have received a copy of the GNU General Public License |
2369 | -# along with this program; if not, write to the Free Software Foundation, |
2370 | -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
2371 | -# |
2372 | -# Parts of this are based on the example python code that ships with |
2373 | -# NetworkManager |
2374 | -# http://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python |
2375 | -# |
2376 | -# Copyright (C) 2012 Canonical, Ltd. |
2377 | - |
2378 | -from subprocess import check_output, CalledProcessError, STDOUT |
2379 | -import sys |
2380 | - |
2381 | -import dbus |
2382 | - |
2383 | -from checkbox_support.parsers.modinfo import ModinfoParser |
2384 | -from checkbox_support.parsers.udevadm import UdevadmParser |
2385 | - |
2386 | - |
2387 | -# This example lists basic information about network interfaces known to NM |
2388 | -devtypes = {1: "Ethernet", |
2389 | - 2: "WiFi", |
2390 | - 5: "Bluetooth", |
2391 | - 6: "OLPC", |
2392 | - 7: "WiMAX", |
2393 | - 8: "Modem"} |
2394 | - |
2395 | -states = {0: "Unknown", |
2396 | - 10: "Unmanaged", |
2397 | - 20: "Unavailable", |
2398 | - 30: "Disconnected", |
2399 | - 40: "Prepare", |
2400 | - 50: "Config", |
2401 | - 60: "Need Auth", |
2402 | - 70: "IP Config", |
2403 | - 80: "IP Check", |
2404 | - 90: "Secondaries", |
2405 | - 100: "Activated", |
2406 | - 110: "Deactivating", |
2407 | - 120: "Failed"} |
2408 | - |
2409 | -attributes = ("category", "interface", "product", "vendor", "driver", "path") |
2410 | - |
2411 | -udev_devices = [] |
2412 | -nm_devices = [] |
2413 | - |
2414 | - |
2415 | -class UdevResult: |
2416 | - def addDevice(self, device): |
2417 | - if device.category == 'NETWORK' and not 'cdc' in device.driver: |
2418 | - udev_devices.append(device) |
2419 | - |
2420 | - |
2421 | -class NetworkingDevice(): |
2422 | - def __init__(self, devtype, props, dev_proxy, bus): |
2423 | - self._devtype = devtype |
2424 | - try: |
2425 | - self._interface = props['Interface'] |
2426 | - except KeyError: |
2427 | - self._interface = "Unknown" |
2428 | - |
2429 | - try: |
2430 | - self._ip = self._int_to_ip(props['Ip4Address']) |
2431 | - except KeyError: |
2432 | - self._ip = "Unknown" |
2433 | - |
2434 | - try: |
2435 | - self._driver = props['Driver'] |
2436 | - except KeyError: |
2437 | - self._driver = "Unknown" |
2438 | - self._driver_ver = "Unknown" |
2439 | - |
2440 | - if self._driver != "Unknown": |
2441 | - self._modinfo = self._modinfo_parser(props['Driver']) |
2442 | - if self._modinfo: |
2443 | - self._driver_ver = self._find_driver_ver() |
2444 | - else: |
2445 | - self._driver_ver = "Unknown" |
2446 | - |
2447 | - try: |
2448 | - self._firmware_missing = props['FirmwareMissing'] |
2449 | - except KeyError: |
2450 | - self._firmware_missing = False |
2451 | - |
2452 | - try: |
2453 | - self._state = states[props['State']] |
2454 | - except KeyError: |
2455 | - self._state = "Unknown" |
2456 | - |
2457 | - def __str__(self): |
2458 | - ret = "Category: %s\n" % self._devtype |
2459 | - ret += "Interface: %s\n" % self._interface |
2460 | - ret += "IP: %s\n" % self._ip |
2461 | - ret += "Driver: %s (ver: %s)\n" % (self._driver, self._driver_ver) |
2462 | - if self._firmware_missing: |
2463 | - ret += "Warning: Required Firmware Missing for device\n" |
2464 | - ret += "State: %s\n" % self._state |
2465 | - return ret |
2466 | - |
2467 | - def getstate(self): |
2468 | - return self._state |
2469 | - |
2470 | - def gettype(self): |
2471 | - return self._devtype |
2472 | - |
2473 | - def _bitrate_to_mbps(self, bitrate): |
2474 | - try: |
2475 | - intbr = int(bitrate) |
2476 | - return str(intbr / 1000) |
2477 | - except Exception: |
2478 | - return "NaN" |
2479 | - |
2480 | - def _modinfo_parser(self, driver): |
2481 | - cmd = ['/sbin/modinfo', driver] |
2482 | - try: |
2483 | - stream = check_output(cmd, stderr=STDOUT, universal_newlines=True) |
2484 | - except CalledProcessError as err: |
2485 | - return None |
2486 | - |
2487 | - if not stream: |
2488 | - return None |
2489 | - else: |
2490 | - parser = ModinfoParser(stream) |
2491 | - modinfo = parser.get_all() |
2492 | - |
2493 | - return modinfo |
2494 | - |
2495 | - def _find_driver_ver(self): |
2496 | - # try the version field first, then vermagic second, some audio |
2497 | - # drivers don't report version if the driver is in-tree |
2498 | - if self._modinfo['version'] and self._modinfo['version'] != 'in-tree:': |
2499 | - return self._modinfo['version'] |
2500 | - else: |
2501 | - # vermagic will look like this (below) and we only care about the |
2502 | - # first part: |
2503 | - # "3.2.0-29-generic SMP mod_unload modversions" |
2504 | - return self._modinfo['vermagic'].split()[0] |
2505 | - |
2506 | - def _int_to_ip(self, int_ip): |
2507 | - ip = [0, 0, 0, 0] |
2508 | - ip[0] = int_ip & 0xff |
2509 | - ip[1] = (int_ip >> 8) & 0xff |
2510 | - ip[2] = (int_ip >> 16) & 0xff |
2511 | - ip[3] = (int_ip >> 24) & 0xff |
2512 | - return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3]) |
2513 | - |
2514 | - |
2515 | -def get_nm_devices(): |
2516 | - devices = [] |
2517 | - bus = dbus.SystemBus() |
2518 | - |
2519 | - # Get a proxy for the base NetworkManager object |
2520 | - proxy = bus.get_object("org.freedesktop.NetworkManager", |
2521 | - "/org/freedesktop/NetworkManager") |
2522 | - manager = dbus.Interface(proxy, "org.freedesktop.NetworkManager") |
2523 | - |
2524 | - # Get all devices known to NM and print their properties |
2525 | - nm_devices = manager.GetDevices() |
2526 | - for d in nm_devices: |
2527 | - dev_proxy = bus.get_object("org.freedesktop.NetworkManager", d) |
2528 | - prop_iface = dbus.Interface(dev_proxy, |
2529 | - "org.freedesktop.DBus.Properties") |
2530 | - props = prop_iface.GetAll("org.freedesktop.NetworkManager.Device") |
2531 | - try: |
2532 | - devtype = devtypes[props['DeviceType']] |
2533 | - except KeyError: |
2534 | - devtype = "Unknown" |
2535 | - |
2536 | - # only return Ethernet devices |
2537 | - if devtype in ("Ethernet"): |
2538 | - devices.append(NetworkingDevice(devtype, props, dev_proxy, bus)) |
2539 | - return devices |
2540 | - |
2541 | - |
2542 | -def match_counts(nm_devices, udev_devices, devtype): |
2543 | - """ |
2544 | - Ensures that the count of devices matching devtype is the same for the |
2545 | - two passed in lists, devices from Network Manager and devices from lspci. |
2546 | - """ |
2547 | - # now check that the count (by type) matches |
2548 | - nm_type_devices = [dev for dev in nm_devices if dev.gettype() in devtype] |
2549 | - udevtype = 'NETWORK' |
2550 | - udev_type_devices = [ |
2551 | - udev |
2552 | - for udev in udev_devices |
2553 | - if udev.category == udevtype] |
2554 | - if len(nm_type_devices) != len(udev_type_devices): |
2555 | - print("ERROR: devices missing - udev showed %d %s devices, but " |
2556 | - "NetworkManager saw %d devices in %s" % |
2557 | - (len(udev_type_devices), udevtype, |
2558 | - len(nm_type_devices), devtype), |
2559 | - file=sys.stderr) |
2560 | - return False |
2561 | - else: |
2562 | - return True |
2563 | - |
2564 | - |
2565 | -def main(args): |
2566 | - try: |
2567 | - output = check_output(['udevadm', 'info', '--export-db']) |
2568 | - except CalledProcessError as err: |
2569 | - raise SystemExit(err) |
2570 | - try: |
2571 | - output = output.decode("UTF-8", errors='ignore') |
2572 | - except UnicodeDecodeError as err: |
2573 | - raise SystemExit("udevadm output is not valid UTF-8") |
2574 | - udev = UdevadmParser(output) |
2575 | - result = UdevResult() |
2576 | - udev.run(result) |
2577 | - |
2578 | - if udev_devices: |
2579 | - print("[ Devices found by udev ]".center(80, '-')) |
2580 | - for device in udev_devices: |
2581 | - for attribute in attributes: |
2582 | - value = getattr(device, attribute) |
2583 | - if value is not None: |
2584 | - if attribute == 'driver': |
2585 | - props = {} |
2586 | - props['Driver'] = value |
2587 | - network_dev = NetworkingDevice(None, props, None, None) |
2588 | - print("%s: %s (ver: %s)" % (attribute.capitalize(), |
2589 | - value, network_dev._driver_ver)) |
2590 | - else: |
2591 | - print("%s: %s" % (attribute.capitalize(), value)) |
2592 | - vendor_id = getattr(device, 'vendor_id') |
2593 | - product_id = getattr(device, 'product_id') |
2594 | - subvendor_id = getattr(device, 'subvendor_id') |
2595 | - subproduct_id = getattr(device, 'subproduct_id') |
2596 | - if vendor_id and product_id: |
2597 | - print("ID: [{0:04x}:{1:04x}]".format(vendor_id, product_id)) |
2598 | - if subvendor_id and subproduct_id: |
2599 | - print("Subsystem ID: [{0:04x}:{1:04x}]".format(subvendor_id, subproduct_id)) |
2600 | - print() |
2601 | - |
2602 | - try: |
2603 | - nm_devices = get_nm_devices() |
2604 | - except dbus.exceptions.DBusException as e: |
2605 | - # server's don't have network manager installed |
2606 | - print("Warning: Exception while talking to Network Manager over dbus." |
2607 | - " Skipping the remainder of this test. If this is a server, this" |
2608 | - " is expected.", file=sys.stderr) |
2609 | - print("The Error Generated was:\n %s" % e, file=sys.stderr) |
2610 | - return 0 |
2611 | - |
2612 | - print("[ Devices found by Network Manager ]".center(80, '-')) |
2613 | - for nm_dev in nm_devices: |
2614 | - print(nm_dev) |
2615 | - |
2616 | - if not match_counts(nm_devices, udev_devices, ("Ethernet")): |
2617 | - return 1 |
2618 | - else: |
2619 | - return 0 |
2620 | - |
2621 | -if __name__ == "__main__": |
2622 | - sys.exit(main(sys.argv[1:])) |
2623 | diff --git a/bin/network_info b/bin/network_info |
2624 | deleted file mode 100755 |
2625 | index 9c20b89..0000000 |
2626 | --- a/bin/network_info |
2627 | +++ /dev/null |
2628 | @@ -1,81 +0,0 @@ |
2629 | -#!/usr/bin/env python3 |
2630 | - |
2631 | -import os |
2632 | -import sys |
2633 | -import subprocess |
2634 | -import socket |
2635 | -import fcntl |
2636 | -import struct |
2637 | - |
2638 | -SYS_PATH = '/sys/class/net' |
2639 | - |
2640 | - |
2641 | -def _read_file(file): |
2642 | - source = open(file, 'r') |
2643 | - content = source.read() |
2644 | - source.close() |
2645 | - return content |
2646 | - |
2647 | - |
2648 | -def get_connected(interface): |
2649 | - STATUS = ('No', 'Yes') |
2650 | - carrier_file = os.path.join(SYS_PATH, interface, 'carrier') |
2651 | - |
2652 | - carrier = 0 |
2653 | - try: |
2654 | - carrier = int(_read_file(carrier_file)) |
2655 | - except IOError: |
2656 | - pass |
2657 | - |
2658 | - return STATUS[carrier] |
2659 | - |
2660 | - |
2661 | -def get_ip_address(interface): |
2662 | - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
2663 | - return socket.inet_ntoa(fcntl.ioctl( |
2664 | - s.fileno(), |
2665 | - 0x8915, # SIOCGIFADDR |
2666 | - struct.pack('256s', interface[:15].encode()) |
2667 | - )[20:24]) |
2668 | - |
2669 | - |
2670 | -def get_ipv6_address(interface): |
2671 | - cmd = ['/sbin/ip', '-6', 'addr', 'show', 'dev', interface] |
2672 | - proc = subprocess.check_output(cmd, universal_newlines=True) |
2673 | - ipaddr = proc.split()[8].strip() |
2674 | - return ipaddr |
2675 | - |
2676 | - |
2677 | -def get_mac_address(interface): |
2678 | - address_file = os.path.join(SYS_PATH, interface, 'address') |
2679 | - |
2680 | - address = '' |
2681 | - try: |
2682 | - address = _read_file(address_file) |
2683 | - except IOError: |
2684 | - pass |
2685 | - |
2686 | - return address |
2687 | - |
2688 | - |
2689 | -def main(args): |
2690 | - for interface in args: |
2691 | - connected = get_connected(interface) |
2692 | - print("Interface: %s" % interface) |
2693 | - print("Connected: %s" % connected) |
2694 | - try: |
2695 | - print("IPv4: %s" % get_ip_address(interface)) |
2696 | - except IOError: |
2697 | - print("IPv4: n/a") |
2698 | - try: |
2699 | - print("IPv6: %s" % get_ipv6_address(interface)) |
2700 | - except IOError: |
2701 | - print("IPv6: n/a") |
2702 | - except: |
2703 | - print("IPv6: n/a") |
2704 | - print("MAC: %s\n" % get_mac_address(interface)) |
2705 | - |
2706 | - return 0 |
2707 | - |
2708 | -if __name__ == "__main__": |
2709 | - sys.exit(main(sys.argv[1:])) |
2710 | diff --git a/bin/network_reconnect_resume_test b/bin/network_reconnect_resume_test |
2711 | deleted file mode 100755 |
2712 | index 8861eb6..0000000 |
2713 | --- a/bin/network_reconnect_resume_test |
2714 | +++ /dev/null |
2715 | @@ -1,102 +0,0 @@ |
2716 | -#!/usr/bin/env python3 |
2717 | - |
2718 | -# Copyright (C) 2012 Canonical, Ltd. |
2719 | - |
2720 | -import re |
2721 | -import subprocess |
2722 | -import argparse |
2723 | -import sys |
2724 | - |
2725 | - |
2726 | -def get_time_difference(device): |
2727 | - """ |
2728 | - Returns the difference in seconds between the last resume from suspend (S3) |
2729 | - and the time it took to reconnect to Wifi. If there is a problem finding |
2730 | - the information, None is returned. |
2731 | - """ |
2732 | - resume_time = get_resume_time() |
2733 | - if resume_time is None: |
2734 | - print("Unable to obtain wakeup/resume time from dmesg." |
2735 | - "Please be sure the system has been suspended", file=sys.stderr) |
2736 | - return None |
2737 | - if device == "wifi": |
2738 | - reconnect_times = list(get_wifi_reconnect_times()) |
2739 | - elif device == "wired": |
2740 | - reconnect_times = list(get_wired_reconnect_times()) |
2741 | - |
2742 | - if not reconnect_times: |
2743 | - print("Unable to obtain %s connection time after a S3. Please be sure" |
2744 | - " that the system has been suspended" % device, file=sys.stderr) |
2745 | - return None |
2746 | - |
2747 | - # since some wifi & wired tests can disconnect and reconnect us multiple |
2748 | - # times after a suspend, we need to find the reconnect that occurs |
2749 | - # immediately after the resume from S3 |
2750 | - for reconnect_time in reconnect_times: |
2751 | - if reconnect_time >= resume_time: |
2752 | - return round((reconnect_time - resume_time), 2) |
2753 | - return None |
2754 | - |
2755 | - |
2756 | -def get_wifi_reconnect_times(): |
2757 | - """ |
2758 | - Returns a list of all the timestamps for wifi reconnects. |
2759 | - """ |
2760 | - data = subprocess.check_output(['dmesg'], universal_newlines=True) |
2761 | - syntax = re.compile("\[(.*)\] wlan.* associated") |
2762 | - results = re.findall(syntax, data) |
2763 | - return map(float, results) |
2764 | - |
2765 | -def get_wired_reconnect_times(): |
2766 | - """ |
2767 | - Returns a list of all the timestamps for wired reconnects. |
2768 | - """ |
2769 | - data = subprocess.check_output(['dmesg'], universal_newlines=True) |
2770 | - syntax = re.compile("\[(.*)\].*eth.* Link is [uU]p") |
2771 | - results = re.findall(syntax, data) |
2772 | - return map(float, results) |
2773 | - |
2774 | - |
2775 | -def get_resume_time(): |
2776 | - """ |
2777 | - Returns the last (most recent) timestamp for an ACPI resume from sleep (S3) |
2778 | - If no resume is found, None is returned. |
2779 | - """ |
2780 | - data = subprocess.check_output(['dmesg'], universal_newlines=True) |
2781 | - syntax = re.compile("\[(.*)\].ACPI: Waking up from system sleep state S3") |
2782 | - results = re.findall(syntax, data) |
2783 | - if not results: |
2784 | - return None |
2785 | - else: |
2786 | - return float(results[-1]) |
2787 | - |
2788 | - |
2789 | -def main(): |
2790 | - parser = argparse.ArgumentParser() |
2791 | - parser.add_argument('-t', '--timeout', |
2792 | - type=int, |
2793 | - help="Specified max time allowed for Wifi/Wired to" |
2794 | - " reconnect in seconds", |
2795 | - required=True) |
2796 | - parser.add_argument('-d', '--device', |
2797 | - help="Specify the device to test either, eth or wlan", |
2798 | - required=True, |
2799 | - choices=['wifi', 'wired']) |
2800 | - args = parser.parse_args() |
2801 | - |
2802 | - timedif = get_time_difference(args.device) |
2803 | - |
2804 | - if not timedif: |
2805 | - return 0 |
2806 | - |
2807 | - print("Your %s resumed in %s seconds after the last suspend" % ( |
2808 | - args.device, timedif)) |
2809 | - if timedif > args.timeout: |
2810 | - print("FAIL: the network failed to reconnect within the allotted time") |
2811 | - return 1 |
2812 | - else: |
2813 | - print("PASS: the network connected within the allotted time") |
2814 | - return 0 |
2815 | - |
2816 | -if __name__ == "__main__": |
2817 | - sys.exit(main()) |
2818 | diff --git a/bin/removable_storage_test b/bin/removable_storage_test |
2819 | deleted file mode 100755 |
2820 | index ddd4e1b..0000000 |
2821 | --- a/bin/removable_storage_test |
2822 | +++ /dev/null |
2823 | @@ -1,870 +0,0 @@ |
2824 | -#!/usr/bin/env python3 |
2825 | - |
2826 | -import argparse |
2827 | -import collections |
2828 | -import dbus |
2829 | -import hashlib |
2830 | -import logging |
2831 | -import os |
2832 | -import re |
2833 | -import shlex |
2834 | -import subprocess |
2835 | -import sys |
2836 | -import tempfile |
2837 | -import time |
2838 | - |
2839 | -import gi |
2840 | -gi.require_version('GUdev', '1.0') |
2841 | -from gi.repository import GUdev |
2842 | - |
2843 | -from checkbox_support.dbus import connect_to_system_bus |
2844 | -from checkbox_support.dbus.udisks2 import UDISKS2_BLOCK_INTERFACE |
2845 | -from checkbox_support.dbus.udisks2 import UDISKS2_DRIVE_INTERFACE |
2846 | -from checkbox_support.dbus.udisks2 import UDISKS2_FILESYSTEM_INTERFACE |
2847 | -from checkbox_support.dbus.udisks2 import UDisks2Model, UDisks2Observer |
2848 | -from checkbox_support.dbus.udisks2 import is_udisks2_supported |
2849 | -from checkbox_support.dbus.udisks2 import lookup_udev_device |
2850 | -from checkbox_support.dbus.udisks2 import map_udisks1_connection_bus |
2851 | -from checkbox_support.heuristics.udisks2 import is_memory_card |
2852 | -from checkbox_support.helpers.human_readable_bytes import HumanReadableBytes |
2853 | -from checkbox_support.parsers.udevadm import CARD_READER_RE |
2854 | -from checkbox_support.parsers.udevadm import GENERIC_RE |
2855 | -from checkbox_support.parsers.udevadm import FLASH_RE |
2856 | -from checkbox_support.parsers.udevadm import find_pkname_is_root_mountpoint |
2857 | -from checkbox_support.udev import get_interconnect_speed |
2858 | -from checkbox_support.udev import get_udev_block_devices |
2859 | -from checkbox_support.udev import get_udev_xhci_devices |
2860 | - |
2861 | - |
2862 | -class ActionTimer(): |
2863 | - '''Class to implement a simple timer''' |
2864 | - def __enter__(self): |
2865 | - self.start = time.time() |
2866 | - return self |
2867 | - |
2868 | - def __exit__(self, *args): |
2869 | - self.stop = time.time() |
2870 | - self.interval = self.stop - self.start |
2871 | - |
2872 | - |
2873 | -class RandomData(): |
2874 | - '''Class to create data files''' |
2875 | - def __init__(self, size): |
2876 | - self.tfile = tempfile.NamedTemporaryFile(delete=False) |
2877 | - self.path = '' |
2878 | - self.name = '' |
2879 | - self.path, self.name = os.path.split(self.tfile.name) |
2880 | - self._write_test_data_file(size) |
2881 | - |
2882 | - def _generate_test_data(self): |
2883 | - seed = "104872948765827105728492766217823438120" |
2884 | - phrase = ''' |
2885 | - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam |
2886 | - nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat |
2887 | - volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation |
2888 | - ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. |
2889 | - Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse |
2890 | - molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero |
2891 | - eros et accumsan et iusto odio dignissim qui blandit praesent luptatum |
2892 | - zzril delenit augue duis dolore te feugait nulla facilisi. |
2893 | - ''' |
2894 | - words = phrase.replace('\n', '').split() |
2895 | - word_deque = collections.deque(words) |
2896 | - seed_deque = collections.deque(seed) |
2897 | - while True: |
2898 | - yield ' '.join(list(word_deque)) |
2899 | - word_deque.rotate(int(seed_deque[0])) |
2900 | - seed_deque.rotate(1) |
2901 | - |
2902 | - def _write_test_data_file(self, size): |
2903 | - data = self._generate_test_data() |
2904 | - while os.path.getsize(self.tfile.name) < size: |
2905 | - self.tfile.write(next(data).encode('UTF-8')) |
2906 | - return self |
2907 | - |
2908 | - |
2909 | -def md5_hash_file(path): |
2910 | - md5 = hashlib.md5() |
2911 | - try: |
2912 | - with open(path, 'rb') as stream: |
2913 | - while True: |
2914 | - data = stream.read(8192) |
2915 | - if not data: |
2916 | - break |
2917 | - md5.update(data) |
2918 | - except IOError as exc: |
2919 | - logging.error("unable to checksum %s: %s", path, exc) |
2920 | - return None |
2921 | - else: |
2922 | - return md5.hexdigest() |
2923 | - |
2924 | - |
2925 | -class DiskTest(): |
2926 | - ''' Class to contain various methods for testing removable disks ''' |
2927 | - |
2928 | - def __init__(self, device, memorycard, lsblkcommand): |
2929 | - self.rem_disks = {} # mounted before the script running |
2930 | - self.rem_disks_nm = {} # not mounted before the script running |
2931 | - self.rem_disks_memory_cards = {} |
2932 | - self.rem_disks_memory_cards_nm = {} |
2933 | - self.rem_disks_speed = {} |
2934 | - # LP: #1313581, TODO: extend to be rem_disks_driver |
2935 | - self.rem_disks_xhci = {} |
2936 | - self.data = '' |
2937 | - self.lsblk = '' |
2938 | - self.device = device |
2939 | - self.memorycard = memorycard |
2940 | - self._run_lsblk(lsblkcommand) |
2941 | - self._probe_disks() |
2942 | - |
2943 | - def read_file(self, source): |
2944 | - with open(source, 'rb') as infile: |
2945 | - try: |
2946 | - self.data = infile.read() |
2947 | - except IOError as exc: |
2948 | - logging.error("Unable to read data from %s: %s", source, exc) |
2949 | - return False |
2950 | - else: |
2951 | - return True |
2952 | - |
2953 | - def write_file(self, data, dest): |
2954 | - try: |
2955 | - outfile = open(dest, 'wb', 0) |
2956 | - except OSError as exc: |
2957 | - logging.error("Unable to open %s for writing.", dest) |
2958 | - logging.error(" %s", exc) |
2959 | - return False |
2960 | - with outfile: |
2961 | - try: |
2962 | - outfile.write(self.data) |
2963 | - except IOError as exc: |
2964 | - logging.error("Unable to write data to %s: %s", dest, exc) |
2965 | - return False |
2966 | - else: |
2967 | - outfile.flush() |
2968 | - os.fsync(outfile.fileno()) |
2969 | - return True |
2970 | - |
2971 | - def clean_up(self, target): |
2972 | - try: |
2973 | - os.unlink(target) |
2974 | - except OSError as exc: |
2975 | - logging.error("Unable to remove tempfile %s", target) |
2976 | - logging.error(" %s", exc) |
2977 | - |
2978 | - def _find_parent(self, device): |
2979 | - if self.lsblk: |
2980 | - pattern = re.compile('KNAME="(?P<KNAME>.*)" ' |
2981 | - 'TYPE="(?P<TYPE>.*)" ' |
2982 | - 'MOUNTPOINT="(?P<MOUNTPOINT>.*)"') |
2983 | - for line in self.lsblk.splitlines(): |
2984 | - m = pattern.match(line) |
2985 | - if m and device.startswith(m.group('KNAME')): |
2986 | - return m.group('KNAME') |
2987 | - return False |
2988 | - |
2989 | - def _run_lsblk(self, lsblkcommand): |
2990 | - try: |
2991 | - self.lsblk = subprocess.check_output(shlex.split(lsblkcommand), |
2992 | - universal_newlines=True) |
2993 | - except subprocess.CalledProcessError as exc: |
2994 | - raise SystemExit(exc) |
2995 | - |
2996 | - def _probe_disks(self): |
2997 | - """ |
2998 | - Internal method used to probe for available disks |
2999 | - |
3000 | - Indirectly sets: |
3001 | - self.rem_disks{,_nm,_memory_cards,_memory_cards_nm,_speed} |
3002 | - """ |
3003 | - if "SNAP" in os.environ: |
3004 | - self._probe_disks_udisks2_cli() |
3005 | - else: |
3006 | - bus, loop = connect_to_system_bus() |
3007 | - if is_udisks2_supported(bus): |
3008 | - self._probe_disks_udisks2(bus) |
3009 | - else: |
3010 | - self._probe_disks_udisks1(bus) |
3011 | - |
3012 | - def _probe_disks_udisks2_cli(self): |
3013 | - # First we will build up a db of udisks info by scraping the output |
3014 | - # of the dump command |
3015 | - # TODO: remove the snap prefix when the alias becomes available |
3016 | - proc = subprocess.Popen(['udisks2.udisksctl', 'dump'], |
3017 | - stdout=subprocess.PIPE) |
3018 | - udisks_devices = {} |
3019 | - current_bd = None |
3020 | - current_interface = None |
3021 | - while True: |
3022 | - line = proc.stdout.readline().decode(sys.stdout.encoding) |
3023 | - if line == '': |
3024 | - break |
3025 | - if line == '\n': |
3026 | - current_bd = None |
3027 | - current_interface = None |
3028 | - if line.startswith('/org/freedesktop/UDisks2/'): |
3029 | - path = line.strip() |
3030 | - current_bd = os.path.basename(path).rstrip(':') |
3031 | - udisks_devices[current_bd] = {} |
3032 | - continue |
3033 | - if current_bd is None: |
3034 | - continue |
3035 | - if line.startswith(' org.freedesktop'): |
3036 | - current_interface = line.strip().rstrip(':') |
3037 | - udisks_devices[current_bd][current_interface] = {} |
3038 | - continue |
3039 | - if current_interface is None: |
3040 | - continue |
3041 | - entry = ''.join(c for c in line if c not in '\n\t\' ') |
3042 | - wanted_keys = ('Device:', 'Drive:', 'MountPoints:', 'Vendor:', |
3043 | - 'ConnectionBus:', 'Model:', 'Media:',) |
3044 | - for key in wanted_keys: |
3045 | - if entry.startswith(key): |
3046 | - udisks_devices[current_bd][current_interface][key] = ( |
3047 | - entry[len(key):]) |
3048 | - |
3049 | - # Now use the populated udisks structure to fill out the API used by |
3050 | - # other _probe disks functions |
3051 | - for device, interfaces in udisks_devices.items(): |
3052 | - # iterate over udisks objects that have both filesystem and |
3053 | - # block device interfaces |
3054 | - if (UDISKS2_FILESYSTEM_INTERFACE in interfaces and |
3055 | - UDISKS2_BLOCK_INTERFACE in interfaces): |
3056 | - # To be an IO candidate there must be a drive object |
3057 | - drive = interfaces[UDISKS2_BLOCK_INTERFACE].get('Drive:') |
3058 | - if drive is None or drive is '/': |
3059 | - continue |
3060 | - drive_object = udisks_devices[os.path.basename(drive)] |
3061 | - |
3062 | - # Get the connection bus property from the drive interface of |
3063 | - # the drive object. This is required to filter out the devices |
3064 | - # we don't want to look at now. |
3065 | - connection_bus = ( |
3066 | - drive_object[UDISKS2_DRIVE_INTERFACE]['ConnectionBus:']) |
3067 | - desired_connection_buses = set([ |
3068 | - map_udisks1_connection_bus(device) |
3069 | - for device in self.device]) |
3070 | - # Skip devices that are attached to undesired connection buses |
3071 | - if connection_bus not in desired_connection_buses: |
3072 | - continue |
3073 | - |
3074 | - dev_file = ( |
3075 | - interfaces[UDISKS2_BLOCK_INTERFACE].get('Device:')) |
3076 | - |
3077 | - parent = self._find_parent(dev_file.replace('/dev/', '')) |
3078 | - if (parent and |
3079 | - find_pkname_is_root_mountpoint(parent, self.lsblk)): |
3080 | - continue |
3081 | - |
3082 | - # XXX: we actually only scrape the first one currently |
3083 | - mount_point = ( |
3084 | - interfaces[UDISKS2_FILESYSTEM_INTERFACE].get( |
3085 | - 'MountPoints:')) |
3086 | - if mount_point == '': |
3087 | - mount_point = None |
3088 | - |
3089 | - # We need to skip-non memory cards if we look for memory cards |
3090 | - # and vice-versa so let's inspect the drive and use heuristics |
3091 | - # to detect memory cards (a memory card reader actually) now. |
3092 | - if self.memorycard != is_memory_card( |
3093 | - drive_object[UDISKS2_DRIVE_INTERFACE]['Vendor:'], |
3094 | - drive_object[UDISKS2_DRIVE_INTERFACE]['Model:'], |
3095 | - drive_object[UDISKS2_DRIVE_INTERFACE]['Media:']): |
3096 | - continue |
3097 | - |
3098 | - if mount_point is None: |
3099 | - self.rem_disks_memory_cards_nm[dev_file] = None |
3100 | - self.rem_disks_nm[dev_file] = None |
3101 | - else: |
3102 | - self.rem_disks_memory_cards[dev_file] = mount_point |
3103 | - self.rem_disks[dev_file] = mount_point |
3104 | - |
3105 | - # Get the speed of the interconnect that is associated with the |
3106 | - # block device we're looking at. This is purely informational |
3107 | - # but it is a part of the required API |
3108 | - udev_devices = get_udev_block_devices(GUdev.Client()) |
3109 | - for udev_device in udev_devices: |
3110 | - if udev_device.get_device_file() == dev_file: |
3111 | - interconnect_speed = get_interconnect_speed(udev_device) |
3112 | - if interconnect_speed: |
3113 | - self.rem_disks_speed[dev_file] = ( |
3114 | - interconnect_speed * 10 ** 6) |
3115 | - else: |
3116 | - self.rem_disks_speed[dev_file] = None |
3117 | - |
3118 | - def _probe_disks_udisks2(self, bus): |
3119 | - """ |
3120 | - Internal method used to probe / discover available disks using udisks2 |
3121 | - dbus interface using the provided dbus bus (presumably the system bus) |
3122 | - """ |
3123 | - # We'll need udisks2 and udev to get the data we need |
3124 | - udisks2_observer = UDisks2Observer() |
3125 | - udisks2_model = UDisks2Model(udisks2_observer) |
3126 | - udisks2_observer.connect_to_bus(bus) |
3127 | - udev_client = GUdev.Client() |
3128 | - # Get a collection of all udev devices corresponding to block devices |
3129 | - udev_devices = get_udev_block_devices(udev_client) |
3130 | - # Get a collection of all udisks2 objects |
3131 | - udisks2_objects = udisks2_model.managed_objects |
3132 | - # Let's get a helper to simplify the loop below |
3133 | - |
3134 | - def iter_filesystems_on_block_devices(): |
3135 | - """ |
3136 | - Generate a collection of UDisks2 object paths that |
3137 | - have both the filesystem and block device interfaces |
3138 | - """ |
3139 | - for udisks2_object_path, interfaces in udisks2_objects.items(): |
3140 | - if (UDISKS2_FILESYSTEM_INTERFACE in interfaces and |
3141 | - UDISKS2_BLOCK_INTERFACE in interfaces): |
3142 | - yield udisks2_object_path |
3143 | - # We need to know about all IO candidates, |
3144 | - # let's iterate over all the block devices reported by udisks2 |
3145 | - for udisks2_object_path in iter_filesystems_on_block_devices(): |
3146 | - # Get interfaces implemented by this object |
3147 | - udisks2_object = udisks2_objects[udisks2_object_path] |
3148 | - # Find the path of the udisks2 object that represents the drive |
3149 | - # this object is a part of |
3150 | - drive_object_path = ( |
3151 | - udisks2_object[UDISKS2_BLOCK_INTERFACE]['Drive']) |
3152 | - # Lookup the drive object, if any. This can fail when |
3153 | - try: |
3154 | - drive_object = udisks2_objects[drive_object_path] |
3155 | - except KeyError: |
3156 | - logging.error( |
3157 | - "Unable to locate drive associated with %s", |
3158 | - udisks2_object_path) |
3159 | - continue |
3160 | - else: |
3161 | - drive_props = drive_object[UDISKS2_DRIVE_INTERFACE] |
3162 | - # Get the connection bus property from the drive interface of the |
3163 | - # drive object. This is required to filter out the devices we don't |
3164 | - # want to look at now. |
3165 | - connection_bus = drive_props["ConnectionBus"] |
3166 | - desired_connection_buses = set([ |
3167 | - map_udisks1_connection_bus(device) |
3168 | - for device in self.device]) |
3169 | - # Skip devices that are attached to undesired connection buses |
3170 | - if connection_bus not in desired_connection_buses: |
3171 | - continue |
3172 | - # Lookup the udev object that corresponds to this object |
3173 | - try: |
3174 | - udev_device = lookup_udev_device(udisks2_object, udev_devices) |
3175 | - except LookupError: |
3176 | - logging.error( |
3177 | - "Unable to locate udev object that corresponds to: %s", |
3178 | - udisks2_object_path) |
3179 | - continue |
3180 | - # Get the block device pathname, |
3181 | - # to avoid the confusion, this is something like /dev/sdbX |
3182 | - dev_file = udev_device.get_device_file() |
3183 | - parent = self._find_parent(dev_file.replace('/dev/', '')) |
3184 | - if parent and find_pkname_is_root_mountpoint(parent, self.lsblk): |
3185 | - continue |
3186 | - # Get the list of mount points of this block device |
3187 | - mount_points = ( |
3188 | - udisks2_object[UDISKS2_FILESYSTEM_INTERFACE]['MountPoints']) |
3189 | - # Get the speed of the interconnect that is associated with the |
3190 | - # block device we're looking at. This is purely informational but |
3191 | - # it is a part of the required API |
3192 | - interconnect_speed = get_interconnect_speed(udev_device) |
3193 | - if interconnect_speed: |
3194 | - self.rem_disks_speed[dev_file] = ( |
3195 | - interconnect_speed * 10 ** 6) |
3196 | - else: |
3197 | - self.rem_disks_speed[dev_file] = None |
3198 | - # We need to skip-non memory cards if we look for memory cards and |
3199 | - # vice-versa so let's inspect the drive and use heuristics to |
3200 | - # detect memory cards (a memory card reader actually) now. |
3201 | - if self.memorycard != is_memory_card(drive_props['Vendor'], |
3202 | - drive_props['Model'], |
3203 | - drive_props['Media']): |
3204 | - continue |
3205 | - # The if/else test below simply distributes the mount_point to the |
3206 | - # appropriate variable, to keep the API requirements. It is |
3207 | - # confusing as _memory_cards is variable is somewhat dummy. |
3208 | - if mount_points: |
3209 | - # XXX: Arbitrarily pick the first of the mount points |
3210 | - mount_point = mount_points[0] |
3211 | - self.rem_disks_memory_cards[dev_file] = mount_point |
3212 | - self.rem_disks[dev_file] = mount_point |
3213 | - else: |
3214 | - self.rem_disks_memory_cards_nm[dev_file] = None |
3215 | - self.rem_disks_nm[dev_file] = None |
3216 | - |
3217 | - def _probe_disks_udisks1(self, bus): |
3218 | - """ |
3219 | - Internal method used to probe / discover available disks using udisks1 |
3220 | - dbus interface using the provided dbus bus (presumably the system bus) |
3221 | - """ |
3222 | - ud_manager_obj = bus.get_object("org.freedesktop.UDisks", |
3223 | - "/org/freedesktop/UDisks") |
3224 | - ud_manager = dbus.Interface(ud_manager_obj, 'org.freedesktop.UDisks') |
3225 | - for dev in ud_manager.EnumerateDevices(): |
3226 | - device_obj = bus.get_object("org.freedesktop.UDisks", dev) |
3227 | - device_props = dbus.Interface(device_obj, dbus.PROPERTIES_IFACE) |
3228 | - udisks = 'org.freedesktop.UDisks.Device' |
3229 | - if not device_props.Get(udisks, "DeviceIsDrive"): |
3230 | - dev_bus = device_props.Get(udisks, "DriveConnectionInterface") |
3231 | - if dev_bus in self.device: |
3232 | - parent_model = parent_vendor = '' |
3233 | - if device_props.Get(udisks, "DeviceIsPartition"): |
3234 | - parent_obj = bus.get_object( |
3235 | - "org.freedesktop.UDisks", |
3236 | - device_props.Get(udisks, "PartitionSlave")) |
3237 | - parent_props = dbus.Interface( |
3238 | - parent_obj, dbus.PROPERTIES_IFACE) |
3239 | - parent_model = parent_props.Get(udisks, "DriveModel") |
3240 | - parent_vendor = parent_props.Get(udisks, "DriveVendor") |
3241 | - parent_media = parent_props.Get(udisks, "DriveMedia") |
3242 | - if self.memorycard: |
3243 | - if (dev_bus != 'sdio' and not |
3244 | - FLASH_RE.search(parent_media) and not |
3245 | - CARD_READER_RE.search(parent_model) and not |
3246 | - GENERIC_RE.search(parent_vendor)): |
3247 | - continue |
3248 | - else: |
3249 | - if (FLASH_RE.search(parent_media) or |
3250 | - CARD_READER_RE.search(parent_model) or |
3251 | - GENERIC_RE.search(parent_vendor)): |
3252 | - continue |
3253 | - dev_file = str(device_props.Get(udisks, "DeviceFile")) |
3254 | - dev_speed = str(device_props.Get(udisks, |
3255 | - "DriveConnectionSpeed")) |
3256 | - self.rem_disks_speed[dev_file] = dev_speed |
3257 | - if len(device_props.Get(udisks, "DeviceMountPaths")) > 0: |
3258 | - devPath = str(device_props.Get(udisks, |
3259 | - "DeviceMountPaths")[0]) |
3260 | - self.rem_disks[dev_file] = devPath |
3261 | - self.rem_disks_memory_cards[dev_file] = devPath |
3262 | - else: |
3263 | - self.rem_disks_nm[dev_file] = None |
3264 | - self.rem_disks_memory_cards_nm[dev_file] = None |
3265 | - |
3266 | - def get_disks_xhci(self): |
3267 | - """ |
3268 | - Compare |
3269 | - 1. the pci slot name of the devices using xhci |
3270 | - 2. the pci slot name of the disks, |
3271 | - which is usb3 disks in this case so far, |
3272 | - to make sure the usb3 disk does be on the controller using xhci |
3273 | - """ |
3274 | - # LP: #1378724 |
3275 | - udev_client = GUdev.Client() |
3276 | - # Get a collection of all udev devices corresponding to block devices |
3277 | - udev_devices = get_udev_block_devices(udev_client) |
3278 | - # Get a collection of all udev devices corresponding to xhci devices |
3279 | - udev_devices_xhci = get_udev_xhci_devices(udev_client) |
3280 | - for udev_device_xhci in udev_devices_xhci: |
3281 | - pci_slot_name = udev_device_xhci.get_property('PCI_SLOT_NAME') |
3282 | - for udev_device in udev_devices: |
3283 | - devpath = udev_device.get_property('DEVPATH') |
3284 | - if (self._compare_pci_slot_from_devpath(devpath, |
3285 | - pci_slot_name)): |
3286 | - self.rem_disks_xhci[ |
3287 | - udev_device.get_property('DEVNAME')] = 'xhci' |
3288 | - return self.rem_disks_xhci |
3289 | - |
3290 | - def mount(self): |
3291 | - passed_mount = {} |
3292 | - |
3293 | - for key in self.rem_disks_nm: |
3294 | - temp_dir = tempfile.mkdtemp() |
3295 | - if self._mount(key, temp_dir) != 0: |
3296 | - logging.error("can't mount %s", key) |
3297 | - else: |
3298 | - passed_mount[key] = temp_dir |
3299 | - |
3300 | - if len(self.rem_disks_nm) == len(passed_mount): |
3301 | - self.rem_disks_nm = passed_mount |
3302 | - return 0 |
3303 | - else: |
3304 | - count = len(self.rem_disks_nm) - len(passed_mount) |
3305 | - self.rem_disks_nm = passed_mount |
3306 | - return count |
3307 | - |
3308 | - def _mount(self, dev_file, mount_point): |
3309 | - return subprocess.call(['mount', dev_file, mount_point]) |
3310 | - |
3311 | - def umount(self): |
3312 | - errors = 0 |
3313 | - for disk in self.rem_disks_nm: |
3314 | - if not self.rem_disks_nm[disk]: |
3315 | - continue |
3316 | - if self._umount(disk) != 0: |
3317 | - errors += 1 |
3318 | - logging.error("can't umount %s on %s", |
3319 | - disk, self.rem_disks_nm[disk]) |
3320 | - return errors |
3321 | - |
3322 | - def _umount(self, mount_point): |
3323 | - # '-l': lazy umount, dealing problem of unable to umount the device. |
3324 | - return subprocess.call(['umount', '-l', mount_point]) |
3325 | - |
3326 | - def clean_tmp_dir(self): |
3327 | - for disk in self.rem_disks_nm: |
3328 | - if not self.rem_disks_nm[disk]: |
3329 | - continue |
3330 | - if not os.path.ismount(self.rem_disks_nm[disk]): |
3331 | - os.rmdir(self.rem_disks_nm[disk]) |
3332 | - |
3333 | - def _compare_pci_slot_from_devpath(self, devpath, pci_slot_name): |
3334 | - # LP: #1334991 |
3335 | - # a smarter parser to get and validate a pci slot name from DEVPATH |
3336 | - # then compare this pci slot name to the other |
3337 | - dl = devpath.split('/') |
3338 | - s = set([x for x in dl if dl.count(x) > 1]) |
3339 | - if ( |
3340 | - (pci_slot_name in dl) and |
3341 | - (dl.index(pci_slot_name) < dl.index('block')) and |
3342 | - (not(pci_slot_name in s)) |
3343 | - ): |
3344 | - # 1. there is such pci_slot_name |
3345 | - # 2. sysfs topology looks like |
3346 | - # DEVPATH = ....../pci_slot_name/....../block/...... |
3347 | - # 3. pci_slot_name should be unique in DEVPATH |
3348 | - return True |
3349 | - else: |
3350 | - return False |
3351 | - |
3352 | - |
3353 | -def main(): |
3354 | - parser = argparse.ArgumentParser() |
3355 | - parser.add_argument('device', |
3356 | - choices=['usb', 'firewire', 'sdio', |
3357 | - 'scsi', 'ata_serial_esata'], |
3358 | - nargs='+', |
3359 | - help=("The type of removable media " |
3360 | - "(usb, firewire, sdio, scsi or ata_serial_esata)" |
3361 | - "to test.")) |
3362 | - parser.add_argument('-l', '--list', |
3363 | - action='store_true', |
3364 | - default=False, |
3365 | - help="List the removable devices and mounting status") |
3366 | - parser.add_argument('-m', '--min-speed', |
3367 | - action='store', |
3368 | - default=0, |
3369 | - type=int, |
3370 | - help="Minimum speed a device must support to be " |
3371 | - "considered eligible for being tested (bits/s)") |
3372 | - parser.add_argument('-p', '--pass-speed', |
3373 | - action='store', |
3374 | - default=0, |
3375 | - type=int, |
3376 | - help="Minimum average throughput from all eligible" |
3377 | - "devices for the test to pass (MB/s)") |
3378 | - parser.add_argument('-i', '--iterations', |
3379 | - action='store', |
3380 | - default='1', |
3381 | - type=int, |
3382 | - help=("The number of test cycles to run. One cycle is" |
3383 | - "comprised of generating --count data files of " |
3384 | - "--size bytes and writing them to each device.")) |
3385 | - parser.add_argument('-c', '--count', |
3386 | - action='store', |
3387 | - default='1', |
3388 | - type=int, |
3389 | - help='The number of random data files to generate') |
3390 | - parser.add_argument('-s', '--size', |
3391 | - action='store', |
3392 | - type=HumanReadableBytes, |
3393 | - default='1MiB', |
3394 | - help=("The size of the test data file to use. " |
3395 | - "You may use SI or IEC suffixes like: 'K', 'M'," |
3396 | - "'G', 'T', 'Ki', 'Mi', 'Gi', 'Ti', etc. Default" |
3397 | - " is %(default)s")) |
3398 | - parser.add_argument('--auto-reduce-size', |
3399 | - action='store_true', |
3400 | - default=False, |
3401 | - help=("Automatically reduce size to fit in the target" |
3402 | - "filesystem. Reducing until fits in 1MiB")) |
3403 | - parser.add_argument('-n', '--skip-not-mount', |
3404 | - action='store_true', |
3405 | - default=False, |
3406 | - help=("skip the removable devices " |
3407 | - "which haven't been mounted before the test.")) |
3408 | - parser.add_argument('--memorycard', action="store_true", |
3409 | - help=("Memory cards devices on bus other than sdio " |
3410 | - "require this parameter to identify " |
3411 | - "them as such")) |
3412 | - parser.add_argument('--driver', |
3413 | - choices=['xhci_hcd'], |
3414 | - help=("Detect the driver of the host controller." |
3415 | - "Only xhci_hcd for usb3 is supported so far.")) |
3416 | - parser.add_argument("--lsblkcommand", action='store', type=str, |
3417 | - default="lsblk -i -n -P -o KNAME,TYPE,MOUNTPOINT", |
3418 | - help=("Command to execute to get lsblk information. " |
3419 | - "Only change it if you know what you're doing.")) |
3420 | - |
3421 | - args = parser.parse_args() |
3422 | - |
3423 | - test = DiskTest(args.device, args.memorycard, args.lsblkcommand) |
3424 | - |
3425 | - errors = 0 |
3426 | - # If we do have removable drives attached and mounted |
3427 | - if len(test.rem_disks) > 0 or len(test.rem_disks_nm) > 0: |
3428 | - if args.list: # Simply output a list of drives detected |
3429 | - print('-' * 20) |
3430 | - print("Removable devices currently mounted:") |
3431 | - if args.memorycard: |
3432 | - if len(test.rem_disks_memory_cards) > 0: |
3433 | - for disk, mnt_point in test.rem_disks_memory_cards.items(): |
3434 | - print("%s : %s" % (disk, mnt_point)) |
3435 | - else: |
3436 | - print("None") |
3437 | - |
3438 | - print("Removable devices currently not mounted:") |
3439 | - if len(test.rem_disks_memory_cards_nm) > 0: |
3440 | - for disk in test.rem_disks_memory_cards_nm: |
3441 | - print(disk) |
3442 | - else: |
3443 | - print("None") |
3444 | - else: |
3445 | - if len(test.rem_disks) > 0: |
3446 | - for disk, mnt_point in test.rem_disks.items(): |
3447 | - print("%s : %s" % (disk, mnt_point)) |
3448 | - else: |
3449 | - print("None") |
3450 | - |
3451 | - print("Removable devices currently not mounted:") |
3452 | - if len(test.rem_disks_nm) > 0: |
3453 | - for disk in test.rem_disks_nm: |
3454 | - print(disk) |
3455 | - else: |
3456 | - print("None") |
3457 | - |
3458 | - print('-' * 20) |
3459 | - |
3460 | - return 0 |
3461 | - |
3462 | - else: # Create a file, copy to disk and compare hashes |
3463 | - if args.skip_not_mount: |
3464 | - disks_all = test.rem_disks |
3465 | - else: |
3466 | - # mount those haven't be mounted yet. |
3467 | - errors_mount = test.mount() |
3468 | - |
3469 | - if errors_mount > 0: |
3470 | - print("There're total %d device(s) failed at mounting." |
3471 | - % errors_mount) |
3472 | - errors += errors_mount |
3473 | - |
3474 | - disks_all = dict(list(test.rem_disks.items()) + |
3475 | - list(test.rem_disks_nm.items())) |
3476 | - |
3477 | - if len(disks_all) > 0: |
3478 | - print("Found the following mounted %s partitions:" |
3479 | - % ', '.join(args.device)) |
3480 | - |
3481 | - for disk, mount_point in disks_all.items(): |
3482 | - supported_speed = test.rem_disks_speed[disk] |
3483 | - print(" %s : %s : %s bits/s" % |
3484 | - (disk, mount_point, supported_speed), |
3485 | - end="") |
3486 | - if (args.min_speed and |
3487 | - int(args.min_speed) > int(supported_speed)): |
3488 | - print(" (Will not test it, speed is below %s bits/s)" % |
3489 | - args.min_speed, end="") |
3490 | - |
3491 | - print("") |
3492 | - |
3493 | - print('-' * 20) |
3494 | - |
3495 | - disks_eligible = {disk: disks_all[disk] for disk in disks_all |
3496 | - if not args.min_speed or |
3497 | - int(test.rem_disks_speed[disk]) >= |
3498 | - int(args.min_speed)} |
3499 | - if len(disks_eligible) == 0: |
3500 | - logging.error( |
3501 | - "No %s disks with speed higher than %s bits/s", |
3502 | - args.device, args.min_speed) |
3503 | - return 1 |
3504 | - write_sizes = [] |
3505 | - test_files = {} |
3506 | - disks_freespace = {} |
3507 | - for disk, path in disks_eligible.items(): |
3508 | - stat = os.statvfs(path) |
3509 | - disks_freespace[disk] = stat.f_bfree * stat.f_bsize |
3510 | - smallest_freespace = min(disks_freespace.values()) |
3511 | - smallest_partition = [d for d, v in disks_freespace.items() if |
3512 | - v == smallest_freespace][0] |
3513 | - desired_size = args.size |
3514 | - if desired_size > smallest_freespace: |
3515 | - if args.auto_reduce_size: |
3516 | - min_space = HumanReadableBytes("1MiB") |
3517 | - if smallest_freespace < min_space: |
3518 | - sys.exit("Not enough space. {} is required on {}" |
3519 | - .format(min_space, smallest_partition)) |
3520 | - new_size = HumanReadableBytes( |
3521 | - int(0.8 * smallest_freespace)) |
3522 | - logging.warning("Automatically reducing test data size" |
3523 | - ". {} requested. Reducing to {}." |
3524 | - .format(desired_size, new_size)) |
3525 | - desired_size = new_size |
3526 | - else: |
3527 | - sys.exit("Not enough space. {} is required on {}" |
3528 | - .format(desired_size, smallest_partition)) |
3529 | - # Generate our data file(s) |
3530 | - for count in range(args.count): |
3531 | - test_files[count] = RandomData(desired_size) |
3532 | - write_sizes.append(os.path.getsize( |
3533 | - test_files[count].tfile.name)) |
3534 | - total_write_size = sum(write_sizes) |
3535 | - |
3536 | - try: |
3537 | - for disk, mount_point in disks_eligible.items(): |
3538 | - print("%s (Total Data Size / iteration: %0.4f MB):" % |
3539 | - (disk, (total_write_size / 1024 / 1024))) |
3540 | - iteration_write_size = ( |
3541 | - total_write_size * args.iterations) / 1024 / 1024 |
3542 | - iteration_write_times = [] |
3543 | - for iteration in range(args.iterations): |
3544 | - target_file_list = [] |
3545 | - write_times = [] |
3546 | - for file_index in range(args.count): |
3547 | - parent_file = test_files[file_index].tfile.name |
3548 | - parent_hash = md5_hash_file(parent_file) |
3549 | - target_filename = ( |
3550 | - test_files[file_index].name + |
3551 | - '.%s' % iteration) |
3552 | - target_path = mount_point |
3553 | - target_file = os.path.join(target_path, |
3554 | - target_filename) |
3555 | - target_file_list.append(target_file) |
3556 | - test.read_file(parent_file) |
3557 | - with ActionTimer() as timer: |
3558 | - if not test.write_file(test.data, |
3559 | - target_file): |
3560 | - logging.error( |
3561 | - "Failed to copy %s to %s", |
3562 | - parent_file, target_file) |
3563 | - errors += 1 |
3564 | - continue |
3565 | - write_times.append(timer.interval) |
3566 | - child_hash = md5_hash_file(target_file) |
3567 | - if parent_hash != child_hash: |
3568 | - logging.warning( |
3569 | - "[Iteration %s] Parent and Child" |
3570 | - " copy hashes mismatch on %s!", |
3571 | - iteration, target_file) |
3572 | - logging.warning( |
3573 | - "\tParent hash: %s", parent_hash) |
3574 | - logging.warning( |
3575 | - "\tChild hash: %s", child_hash) |
3576 | - errors += 1 |
3577 | - for file in target_file_list: |
3578 | - test.clean_up(file) |
3579 | - total_write_time = sum(write_times) |
3580 | - # avg_write_time = total_write_time / args.count |
3581 | - try: |
3582 | - avg_write_speed = (( |
3583 | - total_write_size / total_write_time) / |
3584 | - 1024 / 1024) |
3585 | - except ZeroDivisionError: |
3586 | - avg_write_speed = 0.00 |
3587 | - finally: |
3588 | - iteration_write_times.append(total_write_time) |
3589 | - print("\t[Iteration %s] Average Speed: %0.4f" |
3590 | - % (iteration, avg_write_speed)) |
3591 | - for iteration in range(args.iterations): |
3592 | - iteration_write_time = sum(iteration_write_times) |
3593 | - print("\tSummary:") |
3594 | - print("\t\tTotal Data Attempted: %0.4f MB" |
3595 | - % iteration_write_size) |
3596 | - print("\t\tTotal Time to write: %0.4f secs" |
3597 | - % iteration_write_time) |
3598 | - print("\t\tAverage Write Time: %0.4f secs" % |
3599 | - (iteration_write_time / args.iterations)) |
3600 | - try: |
3601 | - avg_write_speed = (iteration_write_size / |
3602 | - iteration_write_time) |
3603 | - except ZeroDivisionError: |
3604 | - avg_write_speed = 0.00 |
3605 | - finally: |
3606 | - print("\t\tAverage Write Speed: %0.4f MB/s" % |
3607 | - avg_write_speed) |
3608 | - finally: |
3609 | - for key in range(args.count): |
3610 | - test.clean_up(test_files[key].tfile.name) |
3611 | - if (len(test.rem_disks_nm) > 0): |
3612 | - if test.umount() != 0: |
3613 | - errors += 1 |
3614 | - test.clean_tmp_dir() |
3615 | - |
3616 | - if errors > 0: |
3617 | - logging.warning( |
3618 | - "Completed %s test iterations, but there were" |
3619 | - " errors", args.count) |
3620 | - return 1 |
3621 | - else: |
3622 | - # LP: 1313581 |
3623 | - # Try to figure out whether the disk |
3624 | - # is SuperSpeed USB and using xhci_hcd driver. |
3625 | - if (args.driver == 'xhci_hcd'): |
3626 | - # The speed reported by udisks is sometimes |
3627 | - # less than 5G bits/s, for example, |
3628 | - # it may be 705032705 bits/s |
3629 | - # So using |
3630 | - # 500000000 |
3631 | - # = 500 M bits/s |
3632 | - # > 480 M bits/s ( USB 2.0 spec.) |
3633 | - # to make sure that it is higher USB version than 2.0 |
3634 | - # |
3635 | - # int() for int(test.rem_disks_speed[disk]) |
3636 | - # is necessary |
3637 | - # because the speed value of |
3638 | - # the dictionary rem_disks_speed is |
3639 | - # 1. str or int from _probe_disks_udisks2 |
3640 | - # 2. int from _probe_disks_udisks1. |
3641 | - # This is really a mess. : ( |
3642 | - print("\t\t--------------------------------") |
3643 | - if(500000000 < int(test.rem_disks_speed[disk])): |
3644 | - print("\t\tDevice Detected: SuperSpeed USB") |
3645 | - # Unlike rem_disks_speed, |
3646 | - # which must has the connect speed |
3647 | - # for each disk devices, |
3648 | - # disk devices may not use xhci as |
3649 | - # controller drivers. |
3650 | - # This will raise KeyError for no |
3651 | - # associated disk device was found. |
3652 | - xhci_disks = test.get_disks_xhci() |
3653 | - # pep8 style suggest to limit the try clause |
3654 | - # to the absolute minimum amount of code necessary |
3655 | - try: |
3656 | - disk_xhci_flag = xhci_disks[disk] |
3657 | - except KeyError: |
3658 | - print("\t\tDisk does not use xhci_hci.") |
3659 | - return 1 |
3660 | - else: |
3661 | - if('xhci' == disk_xhci_flag): |
3662 | - print("\t\tDriver Detected: xhci_hcd") |
3663 | - else: |
3664 | - print("\t\tDisk does not use xhci_hci.") |
3665 | - logging.debug("disk_xhci_flag is not xhci") |
3666 | - return 1 |
3667 | - else: |
3668 | - # Give it a hint for the detection failure. |
3669 | - # LP: #1362902 |
3670 | - print(("\t\tNo SuperSpeed USB using xhci_hcd " |
3671 | - "was detected correctly.")) |
3672 | - print(("\t\tHint: please use dmesg to check " |
3673 | - "the system status again.")) |
3674 | - return 1 |
3675 | - # Pass is not assured |
3676 | - if (not args.pass_speed or |
3677 | - avg_write_speed >= args.pass_speed): |
3678 | - return 0 |
3679 | - else: |
3680 | - print("FAIL: Average speed was lower than desired " |
3681 | - "pass speed of %s MB/s" % args.pass_speed) |
3682 | - return 1 |
3683 | - else: |
3684 | - logging.error("No device being mounted successfully " |
3685 | - "for testing, aborting") |
3686 | - return 1 |
3687 | - |
3688 | - else: # If we don't have removable drives attached and mounted |
3689 | - logging.error("No removable drives were detected, aborting") |
3690 | - return 1 |
3691 | - |
3692 | -if __name__ == '__main__': |
3693 | - sys.exit(main()) |
3694 | diff --git a/bin/removable_storage_watcher b/bin/removable_storage_watcher |
3695 | deleted file mode 100755 |
3696 | index 88cae9b..0000000 |
3697 | --- a/bin/removable_storage_watcher |
3698 | +++ /dev/null |
3699 | @@ -1,901 +0,0 @@ |
3700 | -#!/usr/bin/env python3 |
3701 | - |
3702 | -import argparse |
3703 | -import collections |
3704 | -import copy |
3705 | -import dbus |
3706 | -import logging |
3707 | -import sys |
3708 | - |
3709 | -import gi |
3710 | -gi.require_version('GUdev', '1.0') |
3711 | -from gi.repository import GObject, GUdev |
3712 | - |
3713 | -from checkbox_support.dbus import connect_to_system_bus |
3714 | -from checkbox_support.dbus.udisks2 import UDisks2Model, UDisks2Observer |
3715 | -from checkbox_support.dbus.udisks2 import is_udisks2_supported |
3716 | -from checkbox_support.dbus.udisks2 import lookup_udev_device |
3717 | -from checkbox_support.dbus.udisks2 import map_udisks1_connection_bus |
3718 | -from checkbox_support.heuristics.udisks2 import is_memory_card |
3719 | -from checkbox_support.parsers.udevadm import CARD_READER_RE, GENERIC_RE, FLASH_RE |
3720 | -from checkbox_support.udev import get_interconnect_speed, get_udev_block_devices |
3721 | - |
3722 | -# Record representing properties of a UDisks1 Drive object needed by the |
3723 | -# UDisks1 version of the watcher implementation |
3724 | -UDisks1DriveProperties = collections.namedtuple( |
3725 | - 'UDisks1DriveProperties', 'file bus speed model vendor media') |
3726 | - |
3727 | -# Delta record that encapsulates difference: |
3728 | -# delta_dir -- directon of the difference, either DELTA_DIR_PLUS or |
3729 | -# DELTA_DIR_MINUS |
3730 | -# value -- the actual value being removed or added, either InterfaceDelta or |
3731 | -# PropertyDelta instance, see below |
3732 | -DeltaRecord = collections.namedtuple("DeltaRecord", "delta_dir value") |
3733 | - |
3734 | -# Delta value for representing interface changes |
3735 | -InterfaceDelta = collections.namedtuple( |
3736 | - "InterfaceDelta", |
3737 | - "delta_type object_path iface_name") |
3738 | - |
3739 | -# Delta value for representing property changes |
3740 | -PropertyDelta = collections.namedtuple( |
3741 | - "PropertyDelta", |
3742 | - "delta_type object_path iface_name prop_name prop_value") |
3743 | - |
3744 | -# Tokens that encode additions and removals |
3745 | -DELTA_DIR_PLUS = '+' |
3746 | -DELTA_DIR_MINUS = '-' |
3747 | - |
3748 | -# Tokens that encode interface and property deltas |
3749 | -DELTA_TYPE_IFACE = 'i' |
3750 | -DELTA_TYPE_PROP = 'p' |
3751 | - |
3752 | - |
3753 | -def format_bytes(size): |
3754 | - """ |
3755 | - Format size to be easily read by humans |
3756 | - |
3757 | - The result is disk-size compatible (using multiples of 10 |
3758 | - rather than 2) string like "4.5GB" |
3759 | - """ |
3760 | - for index, prefix in enumerate(" KMGTPEZY", 0): |
3761 | - factor = 10 ** (index * 3) |
3762 | - if size // factor <= 1000: |
3763 | - break |
3764 | - return "{}{}B".format(size // factor, prefix.strip()) |
3765 | - |
3766 | - |
3767 | -class UDisks1StorageDeviceListener: |
3768 | - |
3769 | - def __init__(self, system_bus, loop, action, devices, minimum_speed, |
3770 | - memorycard): |
3771 | - self._action = action |
3772 | - self._devices = devices |
3773 | - self._minimum_speed = minimum_speed |
3774 | - self._memorycard = memorycard |
3775 | - self._bus = system_bus |
3776 | - self._loop = loop |
3777 | - self._error = False |
3778 | - self._change_cache = [] |
3779 | - |
3780 | - def check(self, timeout): |
3781 | - udisks = 'org.freedesktop.UDisks' |
3782 | - if self._action == 'insert': |
3783 | - signal = 'DeviceAdded' |
3784 | - logging.debug("Adding signal listener for %s.%s", udisks, signal) |
3785 | - self._bus.add_signal_receiver(self.add_detected, |
3786 | - signal_name=signal, |
3787 | - dbus_interface=udisks) |
3788 | - elif self._action == 'remove': |
3789 | - signal = 'DeviceRemoved' |
3790 | - logging.debug("Adding signal listener for %s.%s", udisks, signal) |
3791 | - self._bus.add_signal_receiver(self.remove_detected, |
3792 | - signal_name=signal, |
3793 | - dbus_interface=udisks) |
3794 | - |
3795 | - self._starting_devices = self.get_existing_devices() |
3796 | - logging.debug("Starting with the following devices: %r", |
3797 | - self._starting_devices) |
3798 | - |
3799 | - def timeout_callback(): |
3800 | - print("%s seconds have expired " |
3801 | - "waiting for the device to be inserted." % timeout) |
3802 | - self._error = True |
3803 | - self._loop.quit() |
3804 | - |
3805 | - logging.debug("Adding timeout listener, timeout=%r", timeout) |
3806 | - GObject.timeout_add_seconds(timeout, timeout_callback) |
3807 | - logging.debug("Starting event loop...") |
3808 | - self._loop.run() |
3809 | - |
3810 | - return self._error |
3811 | - |
3812 | - def verify_device_change(self, changed_devices, message=""): |
3813 | - logging.debug("Verifying device change: %s", changed_devices) |
3814 | - # Filter the applicable bus types, as provided on the command line |
3815 | - # (values of self._devices can be 'usb', 'firewire', etc) |
3816 | - desired_bus_devices = [ |
3817 | - device |
3818 | - for device in changed_devices |
3819 | - if device.bus in self._devices] |
3820 | - logging.debug("Desired bus devices: %s", desired_bus_devices) |
3821 | - for dev in desired_bus_devices: |
3822 | - if self._memorycard: |
3823 | - if (dev.bus != 'sdio' |
3824 | - and not FLASH_RE.search(dev.media) |
3825 | - and not CARD_READER_RE.search(dev.model) |
3826 | - and not GENERIC_RE.search(dev.vendor)): |
3827 | - logging.debug("The device does not seem to be a memory" |
3828 | - " card (bus: %r, model: %r), skipping", |
3829 | - dev.bus, dev.model) |
3830 | - return |
3831 | - print(message % {'bus': 'memory card', 'file': dev.file}) |
3832 | - else: |
3833 | - if (FLASH_RE.search(dev.media) |
3834 | - or CARD_READER_RE.search(dev.model) |
3835 | - or GENERIC_RE.search(dev.vendor)): |
3836 | - logging.debug("The device seems to be a memory" |
3837 | - " card (bus: %r (model: %r), skipping", |
3838 | - dev.bus, dev.model) |
3839 | - return |
3840 | - print(message % {'bus': dev.bus, 'file': dev.file}) |
3841 | - if self._minimum_speed: |
3842 | - if dev.speed >= self._minimum_speed: |
3843 | - print("with speed of %(speed)s bits/s " |
3844 | - "higher than %(min_speed)s bits/s" % |
3845 | - {'speed': dev.speed, |
3846 | - 'min_speed': self._minimum_speed}) |
3847 | - else: |
3848 | - print("ERROR: speed of %(speed)s bits/s lower " |
3849 | - "than %(min_speed)s bits/s" % |
3850 | - {'speed': dev.speed, |
3851 | - 'min_speed': self._minimum_speed}) |
3852 | - self._error = True |
3853 | - logging.debug("Device matches requirements, exiting event loop") |
3854 | - self._loop.quit() |
3855 | - |
3856 | - def job_change_detected(self, devices, job_in_progress, job_id, |
3857 | - job_num_tasks, job_cur_task_id, |
3858 | - job_cur_task_percentage): |
3859 | - logging.debug("UDisks1 reports a job change has been detected:" |
3860 | - " devices: %s, job_in_progress: %s, job_id: %s," |
3861 | - " job_num_tasks: %s, job_cur_task_id: %s," |
3862 | - " job_cur_task_percentage: %s", |
3863 | - devices, job_in_progress, job_id, job_num_tasks, |
3864 | - job_cur_task_id, job_cur_task_percentage) |
3865 | - if job_id == "FilesystemMount": |
3866 | - if devices in self._change_cache: |
3867 | - logging.debug("Ignoring filesystem mount," |
3868 | - " the device is present in change cache") |
3869 | - return |
3870 | - logging.debug("Adding devices to change cache: %r", devices) |
3871 | - self._change_cache.append(devices) |
3872 | - logging.debug("Starting devices were: %s", self._starting_devices) |
3873 | - current_devices = self.get_existing_devices() |
3874 | - logging.debug("Current devices are: %s", current_devices) |
3875 | - inserted_devices = list(set(current_devices) - |
3876 | - set(self._starting_devices)) |
3877 | - logging.debug("Computed inserted devices: %s", inserted_devices) |
3878 | - if self._memorycard: |
3879 | - message = "Expected memory card device %(file)s inserted" |
3880 | - else: |
3881 | - message = "Expected %(bus)s device %(file)s inserted" |
3882 | - self.verify_device_change(inserted_devices, |
3883 | - message=message) |
3884 | - |
3885 | - def add_detected(self, added_path): |
3886 | - logging.debug("UDisks1 reports device has been added: %s", added_path) |
3887 | - logging.debug("Resetting change_cache to []") |
3888 | - self._change_cache = [] |
3889 | - signal_name = 'DeviceJobChanged' |
3890 | - dbus_interface = 'org.freedesktop.UDisks' |
3891 | - logging.debug("Adding signal listener for %s.%s", |
3892 | - dbus_interface, signal_name) |
3893 | - self._bus.add_signal_receiver(self.job_change_detected, |
3894 | - signal_name=signal_name, |
3895 | - dbus_interface=dbus_interface) |
3896 | - |
3897 | - def remove_detected(self, removed_path): |
3898 | - logging.debug("UDisks1 reports device has been removed: %s", |
3899 | - removed_path) |
3900 | - |
3901 | - logging.debug("Starting devices were: %s", self._starting_devices) |
3902 | - current_devices = self.get_existing_devices() |
3903 | - logging.debug("Current devices are: %s", current_devices) |
3904 | - removed_devices = list(set(self._starting_devices) - |
3905 | - set(current_devices)) |
3906 | - logging.debug("Computed removed devices: %s", removed_devices) |
3907 | - self.verify_device_change(removed_devices, |
3908 | - message="Removable %(bus)s device %(file)s has been removed") |
3909 | - |
3910 | - def get_existing_devices(self): |
3911 | - logging.debug("Getting existing devices from UDisks1") |
3912 | - ud_manager_obj = self._bus.get_object("org.freedesktop.UDisks", |
3913 | - "/org/freedesktop/UDisks") |
3914 | - ud_manager = dbus.Interface(ud_manager_obj, 'org.freedesktop.UDisks') |
3915 | - existing_devices = [] |
3916 | - for dev in ud_manager.EnumerateDevices(): |
3917 | - try: |
3918 | - device_obj = self._bus.get_object("org.freedesktop.UDisks", |
3919 | - dev) |
3920 | - device_props = dbus.Interface(device_obj, |
3921 | - dbus.PROPERTIES_IFACE) |
3922 | - udisks = 'org.freedesktop.UDisks.Device' |
3923 | - _device_file = device_props.Get(udisks, |
3924 | - "DeviceFile") |
3925 | - _bus = device_props.Get(udisks, |
3926 | - "DriveConnectionInterface") |
3927 | - _speed = device_props.Get(udisks, |
3928 | - "DriveConnectionSpeed") |
3929 | - _parent_model = '' |
3930 | - _parent_media = '' |
3931 | - _parent_vendor = '' |
3932 | - |
3933 | - if device_props.Get(udisks, "DeviceIsPartition"): |
3934 | - parent_obj = self._bus.get_object( |
3935 | - "org.freedesktop.UDisks", |
3936 | - device_props.Get(udisks, "PartitionSlave")) |
3937 | - parent_props = dbus.Interface( |
3938 | - parent_obj, dbus.PROPERTIES_IFACE) |
3939 | - _parent_model = parent_props.Get(udisks, "DriveModel") |
3940 | - _parent_vendor = parent_props.Get(udisks, "DriveVendor") |
3941 | - _parent_media = parent_props.Get(udisks, "DriveMedia") |
3942 | - |
3943 | - if not device_props.Get(udisks, "DeviceIsDrive"): |
3944 | - device = UDisks1DriveProperties( |
3945 | - file=str(_device_file), |
3946 | - bus=str(_bus), |
3947 | - speed=int(_speed), |
3948 | - model=str(_parent_model), |
3949 | - vendor=str(_parent_vendor), |
3950 | - media=str(_parent_media)) |
3951 | - existing_devices.append(device) |
3952 | - |
3953 | - except dbus.DBusException: |
3954 | - pass |
3955 | - |
3956 | - return existing_devices |
3957 | - |
3958 | - |
3959 | -def udisks2_objects_delta(old, new): |
3960 | - """ |
3961 | - Compute the delta between two snapshots of udisks2 objects |
3962 | - |
3963 | - The objects are encoded as {s:{s:{s:v}}} where the first dictionary maps |
3964 | - from DBus object path to a dictionary that maps from interface name to a |
3965 | - dictionary that finally maps from property name to property value. |
3966 | - |
3967 | - The result is a generator of DeltaRecord objects that encodes the changes: |
3968 | - * the 'delta_dir' is either DELTA_DIR_PLUS or DELTA_DIR_MINUS |
3969 | - * the 'value' is a tuple that differs for interfaces and properties. |
3970 | - Interfaces use the format (DELTA_TYPE_IFACE, object_path, iface_name) |
3971 | - while properties use the format (DELTA_TYPE_PROP, object_path, |
3972 | - iface_name, prop_name, prop_value) |
3973 | - |
3974 | - Interfaces are never "changed", they are only added or removed. Properties |
3975 | - can be changed and this is encoded as removal followed by an addition where |
3976 | - both differ only by the 'delta_dir' and the last element of the 'value' |
3977 | - tuple. |
3978 | - """ |
3979 | - # Traverse all objects, old or new |
3980 | - all_object_paths = set() |
3981 | - all_object_paths |= old.keys() |
3982 | - all_object_paths |= new.keys() |
3983 | - for object_path in sorted(all_object_paths): |
3984 | - old_object = old.get(object_path, {}) |
3985 | - new_object = new.get(object_path, {}) |
3986 | - # Traverse all interfaces of each object, old or new |
3987 | - all_iface_names = set() |
3988 | - all_iface_names |= old_object.keys() |
3989 | - all_iface_names |= new_object.keys() |
3990 | - for iface_name in sorted(all_iface_names): |
3991 | - if iface_name not in old_object and iface_name in new_object: |
3992 | - # Report each ADDED interface |
3993 | - assert iface_name in new_object |
3994 | - delta_value = InterfaceDelta( |
3995 | - DELTA_TYPE_IFACE, object_path, iface_name) |
3996 | - yield DeltaRecord(DELTA_DIR_PLUS, delta_value) |
3997 | - # Report all properties ADDED on that interface |
3998 | - for prop_name, prop_value in new_object[iface_name].items(): |
3999 | - delta_value = PropertyDelta(DELTA_TYPE_PROP, object_path, |
4000 | - iface_name, prop_name, |
4001 | - prop_value) |
4002 | - yield DeltaRecord(DELTA_DIR_PLUS, delta_value) |
4003 | - elif iface_name not in new_object and iface_name in old_object: |
4004 | - # Report each REMOVED interface |
4005 | - assert iface_name in old_object |
4006 | - delta_value = InterfaceDelta( |
4007 | - DELTA_TYPE_IFACE, object_path, iface_name) |
4008 | - yield DeltaRecord(DELTA_DIR_MINUS, delta_value) |
4009 | - # Report all properties REMOVED on that interface |
4010 | - for prop_name, prop_value in old_object[iface_name].items(): |
4011 | - delta_value = PropertyDelta(DELTA_TYPE_PROP, object_path, |
4012 | - iface_name, prop_name, |
4013 | - prop_value) |
4014 | - yield DeltaRecord(DELTA_DIR_MINUS, delta_value) |
4015 | - else: |
4016 | - # Analyze properties of each interface that existed both in old |
4017 | - # and new object trees. |
4018 | - assert iface_name in new_object |
4019 | - assert iface_name in old_object |
4020 | - old_props = old_object[iface_name] |
4021 | - new_props = new_object[iface_name] |
4022 | - all_prop_names = set() |
4023 | - all_prop_names |= old_props.keys() |
4024 | - all_prop_names |= new_props.keys() |
4025 | - # Traverse all properties, old or new |
4026 | - for prop_name in sorted(all_prop_names): |
4027 | - if prop_name not in old_props and prop_name in new_props: |
4028 | - # Report each ADDED property |
4029 | - delta_value = PropertyDelta( |
4030 | - DELTA_TYPE_PROP, object_path, iface_name, |
4031 | - prop_name, new_props[prop_name]) |
4032 | - yield DeltaRecord(DELTA_DIR_PLUS, delta_value) |
4033 | - elif prop_name not in new_props and prop_name in old_props: |
4034 | - # Report each REMOVED property |
4035 | - delta_value = PropertyDelta( |
4036 | - DELTA_TYPE_PROP, object_path, iface_name, |
4037 | - prop_name, old_props[prop_name]) |
4038 | - yield DeltaRecord(DELTA_DIR_MINUS, delta_value) |
4039 | - else: |
4040 | - old_value = old_props[prop_name] |
4041 | - new_value = new_props[prop_name] |
4042 | - if old_value != new_value: |
4043 | - # Report each changed property |
4044 | - yield DeltaRecord(DELTA_DIR_MINUS, PropertyDelta( |
4045 | - DELTA_TYPE_PROP, object_path, iface_name, |
4046 | - prop_name, old_value)) |
4047 | - yield DeltaRecord(DELTA_DIR_PLUS, PropertyDelta( |
4048 | - DELTA_TYPE_PROP, object_path, iface_name, |
4049 | - prop_name, new_value)) |
4050 | - |
4051 | - |
4052 | -class UDisks2StorageDeviceListener: |
4053 | - """ |
4054 | - Implementation of the storage device listener concept for UDisks2 backend. |
4055 | - Loosely modeled on the UDisks-based implementation above. |
4056 | - |
4057 | - Implementation details |
4058 | - ^^^^^^^^^^^^^^^^^^^^^^ |
4059 | - |
4060 | - The class, once configured reacts to asynchronous events from the event |
4061 | - loop. Those are either DBus signals or GLib timeout. |
4062 | - |
4063 | - The timeout, if reached, terminates the test and fails with an appropriate |
4064 | - end-user message. The user is expected to manipulate storage devices while |
4065 | - the test is running. |
4066 | - |
4067 | - DBus signals (that correspond to UDisks2 DBus signals) cause callbacks into |
4068 | - this code. Each time a signal is reported "delta" is computed and verified |
4069 | - to determine if there was a successful match. The delta contains a list or |
4070 | - DeltaRecord objects that encode difference (either addition or removal) and |
4071 | - the value of the difference (interface name or interface property value). |
4072 | - This delta is computed by udisks2_objects_delta(). The delta is then passed |
4073 | - to _validate_delta() which has a chance to end the test but also prints |
4074 | - diagnostic messages in verbose mode. This is very useful for understanding |
4075 | - what the test actually sees occurring. |
4076 | - |
4077 | - Insertion/removal detection strategy |
4078 | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
4079 | - |
4080 | - Compared to initial state, the following changes objects need to be |
4081 | - detected |
4082 | - |
4083 | - * At least one UDisks2 object with the following _all_ interfaces: |
4084 | - * UDisks2.Partition |
4085 | - (because we want a partitioned device) |
4086 | - * UDisks2.Block |
4087 | - (because we want that device to have a block device that users can |
4088 | - format) |
4089 | - - having IdUsage == 'filesystem' |
4090 | - (because it should not be a piece of raid or lvm) |
4091 | - - having Size > 0 |
4092 | - (because it should not be and empty removable storage reader) |
4093 | - * UDisks2.Filesystem |
4094 | - (because we want to ensure that a filesystem gets mounted) |
4095 | - - having MountPoints != [] |
4096 | - - as a special exception this rule is REMOVED from eSATA and SATA |
4097 | - devices as they are not automatically mounted anymore. |
4098 | - |
4099 | - This object must be traceable to an UDisks.Drive object: |
4100 | - (because we need the medium to be inserted somewhere) |
4101 | - - having ConnectionBus in (desired_connection_buses) |
4102 | - - as a special exception this rule is weakened for eSATA because |
4103 | - for such devices the ConnectionBus property is empty. |
4104 | - """ |
4105 | - |
4106 | - # Name of the DBus interface exposed UDisks2 for various drives |
4107 | - UDISKS2_DRIVE_INTERFACE = "org.freedesktop.UDisks2.Drive" |
4108 | - |
4109 | - # Name of the DBus property provided by the "Drive" interface above |
4110 | - UDISKS2_DRIVE_PROPERTY_CONNECTION_BUS = "ConnectionBus" |
4111 | - |
4112 | - def __init__(self, system_bus, loop, action, devices, minimum_speed, |
4113 | - memorycard, unmounted = False): |
4114 | - # Store the desired minimum speed of the device in Mbit/s. The argument |
4115 | - # is passed as the number of bits per second so let's fix that. |
4116 | - self._desired_minimum_speed = minimum_speed / 10 ** 6 |
4117 | - # Compute the allowed UDisks2.Drive.ConnectionBus value based on the |
4118 | - # legacy arguments passed from the command line. |
4119 | - self._desired_connection_buses = set([ |
4120 | - map_udisks1_connection_bus(device) for device in devices]) |
4121 | - # Check if we are explicitly looking for memory cards |
4122 | - self._desired_memory_card = memorycard |
4123 | - # Store information whether we also want detected, but unmounted |
4124 | - # devices too |
4125 | - self._allow_unmounted = unmounted |
4126 | - # Store the desired "delta" direction depending on |
4127 | - # whether we test for insertion or removal |
4128 | - if action == "insert": |
4129 | - self._desired_delta_dir = DELTA_DIR_PLUS |
4130 | - elif action == "remove": |
4131 | - self._desired_delta_dir = DELTA_DIR_MINUS |
4132 | - else: |
4133 | - raise ValueError("Unsupported action: {}".format(action)) |
4134 | - # Store DBus bus object as we need to pass it to UDisks2 observer |
4135 | - self._bus = system_bus |
4136 | - # Store event loop object |
4137 | - self._loop = loop |
4138 | - # Setup UDisks2Observer class to track changes published by UDisks2 |
4139 | - self._udisks2_observer = UDisks2Observer() |
4140 | - # Set the initial value of reference_objects. |
4141 | - # The actual value is only set once in check() |
4142 | - self._reference_objects = None |
4143 | - # As above, just initializing in init for sake of consistency |
4144 | - self._is_reference = None |
4145 | - # Setup UDisks2Model to know what the current state is. This is needed |
4146 | - # when remove events are reported as they don't carry enough state for |
4147 | - # the program to work correctly. Since UDisks2Model only applies the |
4148 | - # changes _after_ processing the signals from UDisks2Observer we can |
4149 | - # reliably check all of the properties of the removed object / device. |
4150 | - self._udisks2_model = UDisks2Model(self._udisks2_observer) |
4151 | - # Whenever anything changes call our local change handler |
4152 | - # This handler always computes the full delta (versus the |
4153 | - # reference state) and decides if we have a match or not |
4154 | - self._udisks2_model.on_change.connect(self._on_change) |
4155 | - # We may need an udev context for checking the speed of USB devices |
4156 | - self._udev_client = GUdev.Client() |
4157 | - # A snapshot of udev devices, set in check() |
4158 | - self._reference_udev_devices = None |
4159 | - # Assume the test passes, this is changed when timeout expires or when |
4160 | - # an incorrect device gets inserted. |
4161 | - self._error = False |
4162 | - |
4163 | - def _dump_reference_udisks_objects(self): |
4164 | - logging.debug("Reference UDisks2 objects:") |
4165 | - for udisks2_object in self._reference_objects: |
4166 | - logging.debug(" - %s", udisks2_object) |
4167 | - |
4168 | - def _dump_reference_udev_devices(self): |
4169 | - logging.debug("Reference udev devices:") |
4170 | - for udev_device in self._reference_udev_devices: |
4171 | - interconnect_speed = get_interconnect_speed(udev_device) |
4172 | - if interconnect_speed: |
4173 | - logging.debug(" - %s (USB %dMBit/s)", |
4174 | - udev_device.get_device_file(), |
4175 | - interconnect_speed) |
4176 | - else: |
4177 | - logging.debug(" - %s", udev_device.get_device_file()) |
4178 | - |
4179 | - def check(self, timeout): |
4180 | - """ |
4181 | - Run the configured test and return the result |
4182 | - |
4183 | - The result is False if the test has failed. The timeout, when |
4184 | - non-zero, will make the test fail after the specified seconds have |
4185 | - elapsed without conclusive result. |
4186 | - """ |
4187 | - # Setup a timeout if requested |
4188 | - if timeout > 0: |
4189 | - GObject.timeout_add_seconds(timeout, self._on_timeout_expired) |
4190 | - # Connect the observer to the bus. This will start giving us events |
4191 | - # (actually when the loop starts later below) |
4192 | - self._udisks2_observer.connect_to_bus(self._bus) |
4193 | - # Get the reference snapshot of available devices |
4194 | - self._reference_objects = copy.deepcopy(self._current_objects) |
4195 | - self._dump_reference_udisks_objects() |
4196 | - # Mark the current _reference_objects as ... reference, this is sadly |
4197 | - # needed by _summarize_changes() as it sees the snapshot _after_ a |
4198 | - # change has occurred and cannot determine if the slope of the 'edge' |
4199 | - # of the change. It is purely needed for UI in verbose mode |
4200 | - self._is_reference = True |
4201 | - # A collection of objects that we gladly ignore because we already |
4202 | - # reported on them being somehow inappropriate |
4203 | - self._ignored_objects = set() |
4204 | - # Get the reference snapshot of available udev devices |
4205 | - self._reference_udev_devices = get_udev_block_devices( |
4206 | - self._udev_client) |
4207 | - self._dump_reference_udev_devices() |
4208 | - # Start the loop and wait. The loop will exit either when: |
4209 | - # 1) A proper device has been detected (either insertion or removal) |
4210 | - # 2) A timeout (optional) has expired |
4211 | - self._loop.run() |
4212 | - # Return the outcome of the test |
4213 | - return self._error |
4214 | - |
4215 | - def _on_timeout_expired(self): |
4216 | - """ |
4217 | - Internal function called when the timer expires. |
4218 | - |
4219 | - Basically it's just here to tell the user the test failed or that the |
4220 | - user was unable to alter the device during the allowed time. |
4221 | - """ |
4222 | - print("You have failed to perform the required manipulation in time") |
4223 | - # Fail the test when the timeout was reached |
4224 | - self._error = True |
4225 | - # Stop the loop now |
4226 | - self._loop.quit() |
4227 | - |
4228 | - def _on_change(self): |
4229 | - """ |
4230 | - Internal method called by UDisks2Model whenever a change had occurred |
4231 | - """ |
4232 | - # Compute the changes that had occurred since the reference point |
4233 | - delta_records = list(self._get_delta_records()) |
4234 | - # Display a summary of changes when we are done |
4235 | - self._summarize_changes(delta_records) |
4236 | - # If the changes are what we wanted stop the loop |
4237 | - matching_devices = self._get_matching_devices(delta_records) |
4238 | - if matching_devices: |
4239 | - print("Expected device manipulation complete: {}".format( |
4240 | - ', '.join(matching_devices))) |
4241 | - # And call it a day |
4242 | - self._loop.quit() |
4243 | - |
4244 | - def _get_matching_devices(self, delta_records): |
4245 | - """ |
4246 | - Internal method called that checks if the delta records match the type |
4247 | - of device manipulation we were expecting. Only called from _on_change() |
4248 | - |
4249 | - Returns a set of paths of block devices that matched |
4250 | - """ |
4251 | - # Results |
4252 | - results = set() |
4253 | - # Group changes by DBus object path |
4254 | - grouped_records = collections.defaultdict(list) |
4255 | - for record in delta_records: |
4256 | - grouped_records[record.value.object_path].append(record) |
4257 | - # Create another snapshot od udev devices so that we don't do it over |
4258 | - # and over in the loop below (besides, if we did that then results |
4259 | - # could differ each time). |
4260 | - current_udev_devices = get_udev_block_devices(self._udev_client) |
4261 | - # Iterate over all UDisks2 objects and their delta records |
4262 | - for object_path, records_for_object in grouped_records.items(): |
4263 | - # Skip objects we already ignored and complained about before |
4264 | - if object_path in self._ignored_objects: |
4265 | - continue |
4266 | - needs = set(('block-fs', 'partition', 'non-empty')) |
4267 | - if not self._allow_unmounted: |
4268 | - needs.add('mounted') |
4269 | - |
4270 | - # As a special exception when the ConnectionBus is allowed to be |
4271 | - # empty, as is the case with eSATA devices, do not require the |
4272 | - # filesystem to be mounted as gvfs may choose not to mount it |
4273 | - # automatically. |
4274 | - found = set() |
4275 | - drive_object_path = None |
4276 | - object_block_device = None |
4277 | - for record in records_for_object: |
4278 | - # Skip changes opposite to the ones we need |
4279 | - if record.delta_dir != self._desired_delta_dir: |
4280 | - continue |
4281 | - # For devices with empty "ConnectionBus" property, don't |
4282 | - # require the device to be mounted |
4283 | - if (record.value.iface_name == |
4284 | - "org.freedesktop.UDisks2.Drive" |
4285 | - and record.value.delta_type == DELTA_TYPE_PROP |
4286 | - and record.value.prop_name == "ConnectionBus" |
4287 | - and record.value.prop_value == ""): |
4288 | - needs.remove('mounted') |
4289 | - # Detect block devices designated for filesystems |
4290 | - if (record.value.iface_name == |
4291 | - "org.freedesktop.UDisks2.Block" |
4292 | - and record.value.delta_type == DELTA_TYPE_PROP |
4293 | - and record.value.prop_name == "IdUsage" |
4294 | - and record.value.prop_value == "filesystem"): |
4295 | - found.add('block-fs') |
4296 | - # Memorize the block device path |
4297 | - elif (record.value.iface_name == |
4298 | - "org.freedesktop.UDisks2.Block" |
4299 | - and record.value.delta_type == DELTA_TYPE_PROP |
4300 | - and record.value.prop_name == "PreferredDevice"): |
4301 | - object_block_device = record.value.prop_value |
4302 | - # Ensure the device is a partition |
4303 | - elif (record.value.iface_name == |
4304 | - "org.freedesktop.UDisks2.Partition" |
4305 | - and record.value.delta_type == DELTA_TYPE_IFACE): |
4306 | - found.add('partition') |
4307 | - # Ensure the device is not empty |
4308 | - elif (record.value.iface_name == |
4309 | - "org.freedesktop.UDisks2.Block" |
4310 | - and record.value.delta_type == DELTA_TYPE_PROP |
4311 | - and record.value.prop_name == "Size" |
4312 | - and record.value.prop_value > 0): |
4313 | - found.add('non-empty') |
4314 | - # Ensure the filesystem is mounted |
4315 | - elif (record.value.iface_name == |
4316 | - "org.freedesktop.UDisks2.Filesystem" |
4317 | - and record.value.delta_type == DELTA_TYPE_PROP |
4318 | - and record.value.prop_name == "MountPoints" |
4319 | - and record.value.prop_value != []): |
4320 | - found.add('mounted') |
4321 | - # Finally memorize the drive the block device belongs to |
4322 | - elif (record.value.iface_name == |
4323 | - "org.freedesktop.UDisks2.Block" |
4324 | - and record.value.delta_type == DELTA_TYPE_PROP |
4325 | - and record.value.prop_name == "Drive"): |
4326 | - drive_object_path = record.value.prop_value |
4327 | - logging.debug("Finished analyzing %s, found: %s, needs: %s" |
4328 | - " drive_object_path: %s", object_path, found, needs, |
4329 | - drive_object_path) |
4330 | - if needs != found or drive_object_path is None: |
4331 | - continue |
4332 | - # We've found our candidate, let's look at the drive it belongs |
4333 | - # to. We need to do this as some properties are associated with |
4334 | - # the drive, not the filesystem/block device and the drive may |
4335 | - # not have been inserted at all. |
4336 | - try: |
4337 | - drive_object = self._current_objects[drive_object_path] |
4338 | - except KeyError: |
4339 | - # The drive may be removed along with the device, let's check |
4340 | - # if we originally saw it |
4341 | - try: |
4342 | - drive_object = self._reference_objects[drive_object_path] |
4343 | - except KeyError: |
4344 | - logging.error( |
4345 | - "A block device belongs to a drive we could not find") |
4346 | - logging.error("missing drive: %r", drive_object_path) |
4347 | - continue |
4348 | - try: |
4349 | - drive_props = drive_object["org.freedesktop.UDisks2.Drive"] |
4350 | - except KeyError: |
4351 | - logging.error( |
4352 | - "A block device belongs to an object that is not a Drive") |
4353 | - logging.error("strange object: %r", drive_object_path) |
4354 | - continue |
4355 | - # Ensure the drive is on the appropriate bus |
4356 | - connection_bus = drive_props["ConnectionBus"] |
4357 | - if connection_bus not in self._desired_connection_buses: |
4358 | - logging.warning("The object %r belongs to drive %r that" |
4359 | - " is attached to the bus %r but but we are" |
4360 | - " looking for one of %r so it cannot match", |
4361 | - object_block_device, drive_object_path, |
4362 | - connection_bus, |
4363 | - ", ".join(self._desired_connection_buses)) |
4364 | - # Ignore this object so that we don't spam the user twice |
4365 | - self._ignored_objects.add(object_path) |
4366 | - continue |
4367 | - # Ensure it is a media card reader if this was explicitly requested |
4368 | - drive_is_reader = is_memory_card( |
4369 | - drive_props['Vendor'], drive_props['Model'], |
4370 | - drive_props['Media']) |
4371 | - if self._desired_memory_card and not drive_is_reader: |
4372 | - logging.warning( |
4373 | - "The object %s belongs to drive %s that does not seem to" |
4374 | - " be a media reader", object_block_device, |
4375 | - drive_object_path) |
4376 | - # Ignore this object so that we don't spam the user twice |
4377 | - self._ignored_objects.add(object_path) |
4378 | - continue |
4379 | - # Ensure the desired minimum speed is enforced |
4380 | - if self._desired_minimum_speed: |
4381 | - # We need to discover the speed of the UDisks2 object that is |
4382 | - # about to be matched. Sadly UDisks2 no longer supports this |
4383 | - # property so we need to poke deeper and resort to udev. |
4384 | - # |
4385 | - # The UDisks2 object that we are interested in implements a |
4386 | - # number of interfaces, most notably |
4387 | - # org.freedesktop.UDisks2.Block, that has the Device property |
4388 | - # holding the unix filesystem path (like /dev/sdb1). We already |
4389 | - # hold a reference to that as 'object_block_device' |
4390 | - # |
4391 | - # We take this as a start and attempt to locate the udev Device |
4392 | - # (don't confuse with UDisks2.Device, they are _not_ the same) |
4393 | - # that is associated with that path. |
4394 | - if self._desired_delta_dir == DELTA_DIR_PLUS: |
4395 | - # If we are looking for additions then look at _current_ |
4396 | - # collection of udev devices |
4397 | - udev_devices = current_udev_devices |
4398 | - udisks2_object = self._current_objects[object_path] |
4399 | - else: |
4400 | - # If we are looking for removals then look at referece |
4401 | - # collection of udev devices |
4402 | - udev_devices = self._reference_udev_devices |
4403 | - udisks2_object = self._reference_objects[object_path] |
4404 | - try: |
4405 | - # Try to locate the corresponding udev device among the |
4406 | - # collection we've selected. Use the drive object as the |
4407 | - # key -- this looks for the drive, not partition objects! |
4408 | - udev_device = lookup_udev_device(udisks2_object, |
4409 | - udev_devices) |
4410 | - except LookupError: |
4411 | - logging.error("Unable to map UDisks2 object %s to udev", |
4412 | - object_block_device) |
4413 | - # Ignore this object so that we don't spam the user twice |
4414 | - self._ignored_objects.add(object_path) |
4415 | - continue |
4416 | - interconnect_speed = get_interconnect_speed(udev_device) |
4417 | - # Now that we know the speed of the interconnect we can try to |
4418 | - # validate it against our desired speed. |
4419 | - if interconnect_speed is None: |
4420 | - logging.warning("Unable to determine interconnect speed of" |
4421 | - " device %s", object_block_device) |
4422 | - # Ignore this object so that we don't spam the user twice |
4423 | - self._ignored_objects.add(object_path) |
4424 | - continue |
4425 | - elif interconnect_speed < self._desired_minimum_speed: |
4426 | - logging.warning( |
4427 | - "Device %s is connected via an interconnect that has" |
4428 | - " the speed of %dMbit/s but the required speed was" |
4429 | - " %dMbit/s", object_block_device, interconnect_speed, |
4430 | - self._desired_minimum_speed) |
4431 | - # Ignore this object so that we don't spam the user twice |
4432 | - self._ignored_objects.add(object_path) |
4433 | - continue |
4434 | - else: |
4435 | - logging.info("Device %s is connected via an USB" |
4436 | - " interconnect with the speed of %dMbit/s", |
4437 | - object_block_device, interconnect_speed) |
4438 | - # Yay, success |
4439 | - results.add(object_block_device) |
4440 | - return results |
4441 | - |
4442 | - @property |
4443 | - def _current_objects(self): |
4444 | - return self._udisks2_model.managed_objects |
4445 | - |
4446 | - def _get_delta_records(self): |
4447 | - """ |
4448 | - Internal method used to compute the delta between reference devices and |
4449 | - current devices. The result is a generator of DeltaRecord objects. |
4450 | - """ |
4451 | - assert self._reference_objects is not None, "Only usable after check()" |
4452 | - old = self._reference_objects |
4453 | - new = self._current_objects |
4454 | - return udisks2_objects_delta(old, new) |
4455 | - |
4456 | - def _summarize_changes(self, delta_records): |
4457 | - """ |
4458 | - Internal method used to summarize changes (compared to reference state) |
4459 | - called whenever _on_change() gets called. Only visible in verbose mode |
4460 | - """ |
4461 | - # Filter out anything but interface changes |
4462 | - flat_records = [record |
4463 | - for record in delta_records |
4464 | - if record.value.delta_type == DELTA_TYPE_IFACE] |
4465 | - # Group changes by DBus object path |
4466 | - grouped_records = collections.defaultdict(list) |
4467 | - for record in flat_records: |
4468 | - grouped_records[record.value.object_path].append(record) |
4469 | - # Bail out quickly when nothing got changed |
4470 | - if not flat_records: |
4471 | - if not self._is_reference: |
4472 | - logging.info("You have returned to the reference state") |
4473 | - self._is_reference = True |
4474 | - return |
4475 | - else: |
4476 | - self._is_reference = False |
4477 | - # Iterate over grouped delta records for all objects |
4478 | - logging.info("Compared to the reference state you have:") |
4479 | - for object_path in sorted(grouped_records.keys()): |
4480 | - records_for_object = sorted( |
4481 | - grouped_records[object_path], |
4482 | - key=lambda record: record.value.iface_name) |
4483 | - # Skip any job objects as they just add noise |
4484 | - if any((record.value.iface_name == "org.freedesktop.UDisks2.Job" |
4485 | - for record in records_for_object)): |
4486 | - continue |
4487 | - logging.info("For object %s", object_path) |
4488 | - for record in records_for_object: |
4489 | - # Ignore property changes for now |
4490 | - if record.value.delta_type != DELTA_TYPE_IFACE: |
4491 | - continue |
4492 | - # Get the name of the interface that was affected |
4493 | - iface_name = record.value.iface_name |
4494 | - # Get the properties for that interface (for removals get the |
4495 | - # reference values, for additions get the current values) |
4496 | - if record.delta_dir == DELTA_DIR_PLUS: |
4497 | - props = self._current_objects[object_path][iface_name] |
4498 | - action = "inserted" |
4499 | - else: |
4500 | - props = self._reference_objects[object_path][iface_name] |
4501 | - action = "removed" |
4502 | - # Display some human-readable information associated with each |
4503 | - # interface change |
4504 | - if iface_name == "org.freedesktop.UDisks2.Drive": |
4505 | - logging.info("\t * %s a drive", action) |
4506 | - logging.info("\t vendor and name: %r %r", |
4507 | - props['Vendor'], props['Model']) |
4508 | - logging.info("\t bus: %s", props['ConnectionBus']) |
4509 | - logging.info("\t size: %s", format_bytes(props['Size'])) |
4510 | - logging.info("\t is media card: %s", is_memory_card( |
4511 | - props['Vendor'], props['Model'], props['Media'])) |
4512 | - logging.info("\t current media: %s", |
4513 | - props['Media'] or "???" if |
4514 | - props['MediaAvailable'] else "N/A") |
4515 | - elif iface_name == "org.freedesktop.UDisks2.Block": |
4516 | - logging.info("\t * %s block device", action) |
4517 | - logging.info("\t from drive: %s", props['Drive']) |
4518 | - logging.info("\t having device: %s", props['Device']) |
4519 | - logging.info("\t having usage, type and version:" |
4520 | - " %s %s %s", props['IdUsage'], |
4521 | - props['IdType'], props['IdVersion']) |
4522 | - logging.info("\t having label: %s", props['IdLabel']) |
4523 | - elif iface_name == "org.freedesktop.UDisks2.PartitionTable": |
4524 | - logging.info("\t * %s partition table", action) |
4525 | - logging.info("\t having type: %r", props['Type']) |
4526 | - elif iface_name == "org.freedesktop.UDisks2.Partition": |
4527 | - logging.info("\t * %s partition", action) |
4528 | - logging.info("\t from partition table: %s", |
4529 | - props['Table']) |
4530 | - logging.info("\t having size: %s", |
4531 | - format_bytes(props['Size'])) |
4532 | - logging.info("\t having name: %r", props['Name']) |
4533 | - elif iface_name == "org.freedesktop.UDisks2.Filesystem": |
4534 | - logging.info("\t * %s file system", action) |
4535 | - logging.info("\t having mount points: %r", |
4536 | - props['MountPoints']) |
4537 | - |
4538 | - |
4539 | -def main(): |
4540 | - description = "Wait for the specified device to be inserted or removed." |
4541 | - parser = argparse.ArgumentParser(description=description) |
4542 | - parser.add_argument('action', choices=['insert', 'remove']) |
4543 | - parser.add_argument('device', choices=['usb', 'sdio', 'firewire', 'scsi', |
4544 | - 'ata_serial_esata'], nargs="+") |
4545 | - memorycard_help = ("Memory cards devices on bus other than sdio require " |
4546 | - "this parameter to identify them as such") |
4547 | - parser.add_argument('--memorycard', action="store_true", |
4548 | - help=memorycard_help) |
4549 | - parser.add_argument('--timeout', type=int, default=20) |
4550 | - min_speed_help = ("Will only accept a device if its connection speed " |
4551 | - "attribute is higher than this value " |
4552 | - "(in bits/s)") |
4553 | - parser.add_argument('--minimum_speed', '-m', help=min_speed_help, |
4554 | - type=int, default=0) |
4555 | - parser.add_argument('--verbose', action='store_const', const=logging.INFO, |
4556 | - dest='logging_level', help="Enable verbose output") |
4557 | - parser.add_argument('--debug', action='store_const', const=logging.DEBUG, |
4558 | - dest='logging_level', help="Enable debugging") |
4559 | - parser.add_argument('--unmounted', action='store_true', |
4560 | - help="Don't require drive being automounted") |
4561 | - parser.set_defaults(logging_level=logging.WARNING) |
4562 | - args = parser.parse_args() |
4563 | - |
4564 | - # Configure logging as requested |
4565 | - # XXX: This may be incorrect as logging.basicConfig() fails after any other |
4566 | - # call to logging.log(). The proper solution is to setup a verbose logging |
4567 | - # configuration and I didn't want to do it now. |
4568 | - logging.basicConfig( |
4569 | - level=args.logging_level, |
4570 | - format='[%(asctime)s] %(levelname)s:%(name)s:%(message)s') |
4571 | - |
4572 | - # Connect to the system bus, we also get the event |
4573 | - # loop as we need it to start listening for signals. |
4574 | - system_bus, loop = connect_to_system_bus() |
4575 | - |
4576 | - # Check if system bus has the UDisks2 object |
4577 | - if is_udisks2_supported(system_bus): |
4578 | - # Construct the listener with all of the arguments provided on the |
4579 | - # command line and the explicit system_bus, loop objects. |
4580 | - logging.debug("Using UDisks2 interface") |
4581 | - listener = UDisks2StorageDeviceListener( |
4582 | - system_bus, loop, |
4583 | - args.action, args.device, args.minimum_speed, args.memorycard, |
4584 | - args.unmounted) |
4585 | - else: |
4586 | - # Construct the listener with all of the arguments provided on the |
4587 | - # command line and the explicit system_bus, loop objects. |
4588 | - logging.debug("Using UDisks1 interface") |
4589 | - listener = UDisks1StorageDeviceListener( |
4590 | - system_bus, loop, |
4591 | - args.action, args.device, args.minimum_speed, args.memorycard) |
4592 | - # Run the actual listener and wait till it either times out of discovers |
4593 | - # the appropriate media changes |
4594 | - try: |
4595 | - return listener.check(args.timeout) |
4596 | - except KeyboardInterrupt: |
4597 | - return 1 |
4598 | - |
4599 | -if __name__ == "__main__": |
4600 | - sys.exit(main()) |
4601 | diff --git a/bin/sleep_test_log_check b/bin/sleep_test_log_check |
4602 | deleted file mode 100755 |
4603 | index aae7556..0000000 |
4604 | --- a/bin/sleep_test_log_check |
4605 | +++ /dev/null |
4606 | @@ -1,201 +0,0 @@ |
4607 | -#!/usr/bin/env python3 |
4608 | -# -*- coding: utf-8 -*- |
4609 | -# |
4610 | -# This file is part of Checkbox. |
4611 | -# |
4612 | -# Copyright 2014 Canonical Ltd. |
4613 | -# |
4614 | -# Authors |
4615 | -# Jeff Lane <jeffrey.lane@canonical.com> |
4616 | -# Daniel Manrique<daniel.manrique@canonical.com> |
4617 | -# |
4618 | -# Checkbox is free software: you can redistribute it and/or modify |
4619 | -# it under the terms of the GNU General Public License version 3, |
4620 | -# as published by the Free Software Foundation. |
4621 | -# |
4622 | -# Checkbox is distributed in the hope that it will be useful, |
4623 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4624 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4625 | -# GNU General Public License for more details. |
4626 | -# |
4627 | -# You should have received a copy of the GNU General Public License |
4628 | -# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
4629 | - |
4630 | - |
4631 | -''' |
4632 | -This script is used to parse the log generated by fwts and check it for certain |
4633 | -errors detected during testing. It expects that this is a log file created by |
4634 | -fwts at runtime using the -l <log name> option. |
4635 | - |
4636 | -It's written now specifically for checking ater the fwts s3 and s4 tests but |
4637 | -can be adapted to look for other tests, or all tests. |
4638 | -''' |
4639 | - |
4640 | -import sys |
4641 | -import collections |
4642 | -import re |
4643 | - |
4644 | -from argparse import ArgumentParser |
4645 | -import logging |
4646 | - |
4647 | -# Definitions of when a level starts, how a failure looks, |
4648 | -# and when a level ends. |
4649 | -start_level_re = r'^(?P<level>.+) failures: (?P<numfails>NONE|\d+)$' |
4650 | -start_level_re = re.compile(start_level_re) |
4651 | -failure_re = re.compile(r'^ (?P<test>(s3|s4)): (?P<details>.+)$') |
4652 | -end_level_re = re.compile(r"$^") |
4653 | - |
4654 | - |
4655 | -def parse_summary(summary, results): |
4656 | - """ |
4657 | - Parses an entire "Test Failure Summary" section, which contains a short |
4658 | - summary of failures observed per level. Returns nothing, but adds the |
4659 | - results to the passed results dictionary. |
4660 | - |
4661 | - :param summary: |
4662 | - A list of lines comprised in this summary section |
4663 | - |
4664 | - :param results: |
4665 | - The results dictionary into which to put the end result. Should be a |
4666 | - dict with keys for each level, the values are dicts with keys for each |
4667 | - test (s3, s4) which in turn contain a list of all the failures observed |
4668 | - for that level and test. |
4669 | - """ |
4670 | - current_level = None |
4671 | - current_acum = [] |
4672 | - |
4673 | - for logline in summary: |
4674 | - level_matches = start_level_re.search(logline) |
4675 | - if level_matches: |
4676 | - logging.debug("Found a level: %s", level_matches.group('level')) |
4677 | - current_level = level_matches.group('level') |
4678 | - elif end_level_re.search(logline) and current_level: |
4679 | - if current_level: |
4680 | - logging.debug("Current level (%s) has %s", |
4681 | - current_level, current_acum) |
4682 | - # By passing results[current_level] a key in results will be |
4683 | - # created for every level we see, regardless of whether it |
4684 | - # reports failures or not. This is OK because we can later |
4685 | - # check results' keys to ensure we saw at least one level; if |
4686 | - # results has no keys, it could mean a malformed fwts log file. |
4687 | - parse_level(current_acum, results[current_level]) |
4688 | - else: |
4689 | - logging.debug("Discarding junk") |
4690 | - current_acum = [] |
4691 | - current_level = None |
4692 | - else: |
4693 | - current_acum.append(logline) |
4694 | - |
4695 | - |
4696 | -def parse_level(level_lines, level_results): |
4697 | - """ |
4698 | - Parses the level's lines, appending the failures to the level's results. |
4699 | - level_results is a dictionary with a key per test type (s3, s4, and so on). |
4700 | - Returns nothing, but adds the results to the passed results dictionary for |
4701 | - this level. |
4702 | - |
4703 | - :param level_lines: |
4704 | - A list of lines comprised in this level's list of failures. |
4705 | - |
4706 | - : param level_results: |
4707 | - A dictionary containing this level's results. Should be a dict with |
4708 | - keys for each test, to which the failures for the level will be |
4709 | - appended. |
4710 | - """ |
4711 | - for failureline in level_lines: |
4712 | - failure_matches = failure_re.search(failureline) |
4713 | - if failure_matches: |
4714 | - test = failure_matches.group('test') |
4715 | - details = failure_matches.group('details') |
4716 | - logging.debug("fail %s was %s", test, details) |
4717 | - level_results[test].append(details) |
4718 | - |
4719 | - |
4720 | -def main(): |
4721 | - parser = ArgumentParser() |
4722 | - parser.add_argument('-d', '--debug', |
4723 | - action='store_const', |
4724 | - const=logging.DEBUG, |
4725 | - default=logging.INFO, |
4726 | - help="Show debugging information.") |
4727 | - parser.add_argument('-v', '--verbose', |
4728 | - action='store_true', |
4729 | - default=False, |
4730 | - help="Display each error discovered. May provide \ |
4731 | - very long output. Also, this option will only \ |
4732 | - provide a list of UNIQUE errors encountered in \ |
4733 | - the log file. It will not display duplicates. \ |
4734 | - Default is [%(default)s]") |
4735 | - parser.add_argument('test', |
4736 | - action='store', |
4737 | - choices=['s3', 's4'], |
4738 | - help='The test to check (s3 or s4)') |
4739 | - parser.add_argument('logfile', |
4740 | - action='store', |
4741 | - help='The log file to parse') |
4742 | - |
4743 | - args = parser.parse_args() |
4744 | - |
4745 | - logging.basicConfig(level=args.debug) |
4746 | - |
4747 | - #Create a generator and get our lines |
4748 | - log = (line.rstrip() for line in open(args.logfile, 'rt', encoding="UTF-8")) |
4749 | - |
4750 | - # End result will be a dictionary with a key per level, value is another |
4751 | - # dictionary with a key per test (s3, s4, ...) and a list of all failures |
4752 | - # for each test. Duplicates are possible, because we should also indicate |
4753 | - # the number of instances for each failure. |
4754 | - results = collections.defaultdict(lambda: collections.defaultdict(list)) |
4755 | - |
4756 | - sum_acum = [] |
4757 | - summaries_found = 0 |
4758 | - |
4759 | - # Start parsing the fwts log. Gather each "Test Failure Summary" section |
4760 | - # and when it's complete, pass it to the parse_summary function to extract |
4761 | - # levels and tests. |
4762 | - for logline in log: |
4763 | - if "Test Failure Summary" in logline: |
4764 | - parse_summary(sum_acum, results) |
4765 | - summaries_found += 1 |
4766 | - sum_acum = [] |
4767 | - else: |
4768 | - sum_acum.append(logline) |
4769 | - # We reached the end, so add the last accumulated summary |
4770 | - if sum_acum: |
4771 | - parse_summary(sum_acum, results) |
4772 | - |
4773 | - # Report what I found |
4774 | - for level in sorted(results.keys()): |
4775 | - if results[level]: # Yes, we can have an empty level. We may have |
4776 | - # seen the levelheader but had it report no |
4777 | - # failures. |
4778 | - print("{} failures:".format(level)) |
4779 | - for test in results[level].keys(): |
4780 | - print(" {}: {} failures".format(test, |
4781 | - len(results[level][test]))) |
4782 | - if args.verbose: |
4783 | - print('='*40) |
4784 | - counts = collections.Counter(results[level][test]) |
4785 | - for failure in counts: |
4786 | - print(" {} (x {})".format(failure, counts[failure])) |
4787 | - |
4788 | - # Decide on the outcome based on the collected information |
4789 | - if not summaries_found: |
4790 | - logging.error("No fwts test summaries found, " |
4791 | - "possible malformed fwts log file") |
4792 | - return_code = 2 |
4793 | - elif not results.keys(): # If it has no keys, means we didn't see any |
4794 | - # FWTS levels |
4795 | - logging.error("None of the summaries contained failure levels, " |
4796 | - "possible malformed fwts log file") |
4797 | - return_code = 2 |
4798 | - elif any(results.values()): # If any of the results' levels has errors |
4799 | - return_code = 1 |
4800 | - else: |
4801 | - print("No errors detected") |
4802 | - return_code = 0 |
4803 | - |
4804 | - return return_code |
4805 | - |
4806 | -if __name__ == '__main__': |
4807 | - sys.exit(main()) |
4808 | diff --git a/bin/storage_test b/bin/storage_test |
4809 | deleted file mode 100755 |
4810 | index f96f28d..0000000 |
4811 | --- a/bin/storage_test |
4812 | +++ /dev/null |
4813 | @@ -1,94 +0,0 @@ |
4814 | -#!/bin/bash |
4815 | - |
4816 | -# take the path of the storage device and test is it a block device. |
4817 | - |
4818 | -function run_bonnie() { |
4819 | - echo "Running bonnie++ on $1..." |
4820 | - mount_point=$(df -h | grep -m 1 $1 | awk '{print $6}') |
4821 | - echo "Putting scratch disk at $mount_point" |
4822 | - mkdir -p "$mount_point/tmp/scratchdir" |
4823 | - |
4824 | - # When running on disks with small drives (SSD/flash) we need to do |
4825 | - # some tweaking. Bonnie uses 2x RAM by default to write data. If that's |
4826 | - # more than available disk space, the test will fail inappropriately. |
4827 | - free_space=$(df -m | grep -m 1 $1 | awk '{print $4}') |
4828 | - echo " Disk $1 has ${free_space}MB available" |
4829 | - memory=$(free -m |grep Mem|awk '{print $2}') |
4830 | - echo " System has ${memory}MB RAM" |
4831 | - disk_needed=$((memory * 2)) |
4832 | - echo " We need ${disk_needed}MB of disk space for testing" |
4833 | - if [[ "$disk_needed" -ge "$free_space" ]]; then |
4834 | - echo " Insufficient disk space available for defaults." |
4835 | - echo " reducing memory footprint to be 1/2 of free disk space." |
4836 | - # we need to pass an amount that's 1/2 the amount of available disk |
4837 | - # space to bonnie++ so she doesn't overwrite and fail. |
4838 | - disk_needed=$(($free_space/2)) |
4839 | - bonnie++ -d $mount_point/tmp/scratchdir -u root -r $disk_needed |
4840 | - else |
4841 | - echo " Free disk space is sufficient to continue testing." |
4842 | - bonnie++ -d $mount_point/tmp/scratchdir -u root |
4843 | - fi |
4844 | -} |
4845 | - |
4846 | -disk=/dev/$1 |
4847 | - |
4848 | -if [ -b "$disk" ] |
4849 | -then |
4850 | - echo "$disk is a block device" |
4851 | - |
4852 | - #Add a check for warnings |
4853 | - WARN=$(parted -s ${disk} print | grep "^Warning.*${disk}.*[Rr]ead-only" 2>&1) |
4854 | - if [[ $? == 0 ]] |
4855 | - then |
4856 | - echo "Warning found in parted output:" |
4857 | - echo $WARN |
4858 | - echo "Aborting Test" |
4859 | - exit 1 |
4860 | - fi |
4861 | - |
4862 | - # Regex changed to better handle when $disk appears more than once |
4863 | - # in parted output (such as in warning messages or not caught in the |
4864 | - # check above) |
4865 | - size=`parted -l -s 2>&1 | grep "Disk .*${disk}:" | awk '{print $3}'` |
4866 | - |
4867 | - if [ -n "$size" ] |
4868 | - then |
4869 | - echo "$disk reports a size of $size." |
4870 | - # Have to account for the end of the size descriptor |
4871 | - size_range=${size:(-2)} |
4872 | - |
4873 | - if mount | grep -q $disk |
4874 | - then |
4875 | - echo "$disk is mounted, proceeding." |
4876 | - else |
4877 | - echo "$disk is not mounted. It must be mounted before testing." |
4878 | - exit 1 |
4879 | - fi |
4880 | - |
4881 | - |
4882 | - if [ "$size_range" == "KB" ] |
4883 | - then |
4884 | - echo "$disk size reported in KB, seems to be too small for testing." |
4885 | - exit 1 |
4886 | - elif [ "$size_range" == "MB" ] |
4887 | - then |
4888 | - size_int=${size::${#size}-2} |
4889 | - |
4890 | - if [ "$size_int" -gt 10 ] |
4891 | - then |
4892 | - run_bonnie $disk |
4893 | - else |
4894 | - echo "$disk is too small to be used for testing." |
4895 | - exit 1 |
4896 | - fi |
4897 | - else |
4898 | - run_bonnie $disk |
4899 | - fi |
4900 | - else |
4901 | - echo "$disk doesn't report a size." |
4902 | - exit 1 |
4903 | - fi |
4904 | -else |
4905 | - echo "$disk is not listed as a block device." |
4906 | - exit 1 |
4907 | -fi |
4908 | diff --git a/bin/test_bt_keyboard b/bin/test_bt_keyboard |
4909 | index 8828b3a..1457a1d 100755 |
4910 | --- a/bin/test_bt_keyboard |
4911 | +++ b/bin/test_bt_keyboard |
4912 | @@ -5,7 +5,7 @@ |
4913 | # Written by: |
4914 | # Maciej Kisielewski <maciej.kisielewski@canonical.com> |
4915 | |
4916 | -import bt_helper |
4917 | +import checkbox_support.bt_helper |
4918 | |
4919 | |
4920 | def main(): |
4921 | diff --git a/bin/xml_sanitize b/bin/xml_sanitize |
4922 | deleted file mode 100755 |
4923 | index 0cdbc8e..0000000 |
4924 | --- a/bin/xml_sanitize |
4925 | +++ /dev/null |
4926 | @@ -1,46 +0,0 @@ |
4927 | -#!/usr/bin/python3 |
4928 | -import errno |
4929 | -import io |
4930 | -import sys |
4931 | - |
4932 | -from argparse import ArgumentParser, FileType |
4933 | - |
4934 | -VALID_XML_CHARS = frozenset([0x9, 0xA, 0xD] + |
4935 | - list(range(0x20, 0xD7FF)) + |
4936 | - list(range(0xE000, 0xFFFD)) + |
4937 | - list(range(0x10000, 0x10FFFF))) |
4938 | - |
4939 | - |
4940 | -def is_valid_xml_char(ch): |
4941 | - # Is this character valid in XML? |
4942 | - # http://www.w3.org/TR/xml/#charsets |
4943 | - return ord(ch) in VALID_XML_CHARS |
4944 | - |
4945 | - |
4946 | -def main(): |
4947 | - parser = ArgumentParser("Receives as input some text and outputs " |
4948 | - "the same text without characters which are " |
4949 | - "not valid in the XML specification.") |
4950 | - parser.add_argument('input_file', |
4951 | - type=FileType('r'), |
4952 | - nargs='?', |
4953 | - help='The name of the file to sanitize.') |
4954 | - args = parser.parse_args() |
4955 | - |
4956 | - if args.input_file: |
4957 | - text = ''.join([c for c in args.input_file.read() if |
4958 | - is_valid_xml_char(c)]) |
4959 | - |
4960 | - else: |
4961 | - with io.TextIOWrapper( |
4962 | - sys.stdin.buffer, encoding='UTF-8', errors="ignore") as stdin: |
4963 | - text = ''.join([c for c in stdin.read() if is_valid_xml_char(c)]) |
4964 | - |
4965 | - print(text) |
4966 | - |
4967 | -if __name__ == "__main__": |
4968 | - try: |
4969 | - sys.exit(main()) |
4970 | - except Exception as err: |
4971 | - if err.errno != errno.EPIPE: |
4972 | - raise(err) |
4973 | diff --git a/manage.py b/manage.py |
4974 | index 2b84d17..b16b593 100755 |
4975 | --- a/manage.py |
4976 | +++ b/manage.py |
4977 | @@ -247,7 +247,6 @@ class InstallSpecials(InstallCommand): |
4978 | def invoked(self, ns): |
4979 | super().invoked(ns) |
4980 | self._install_kernel_security_specials(ns) |
4981 | - self._install_bt_helper(ns) |
4982 | |
4983 | def _install_kernel_security_specials(self, ns): |
4984 | dest_map = self._get_dest_map(ns.layout, ns.prefix) |
4985 | @@ -266,13 +265,6 @@ class InstallSpecials(InstallCommand): |
4986 | os.path.join(provider.bin_dir, 'test_kernel_security.py'), |
4987 | ns.root + os.path.join(dest_map['data'], 'test_kernel_security.py')) |
4988 | |
4989 | - def _install_bt_helper(self, ns): |
4990 | - dest_map = self._get_dest_map(ns.layout, ns.prefix) |
4991 | - provider = self.get_provider() |
4992 | - shutil.copy( |
4993 | - os.path.join(provider.bin_dir, 'bt_helper.py'), |
4994 | - ns.root + dest_map['bin']) |
4995 | - |
4996 | |
4997 | def _copytree(src, dst): |
4998 | """Simple copytree that always overwrites.""" |
4999 | diff --git a/requirements/container-tests-provider-snappy b/requirements/container-tests-provider-snappy |
5000 | index 97b8b5f..d0b77e7 100755 |
\o/