Merge ~sylvain-pineau/plainbox-provider-snappy:cleanup_duplicates into plainbox-provider-snappy:master

Proposed by Sylvain Pineau
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)
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

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

To post a comment you must log in.
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

\o/

review: Approve
Revision history for this message
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.

review: Needs Fixing
Revision history for this message
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+5614minor)pagefaults 0swaps
[trusty] provisioning container
[trusty] (timing) 37.27user 14.30system 1:27.76elapsed 58%CPU (0avgtext+0avgdata 69364maxresident)k
[trusty] (timing) 0inputs+1616624outputs (16major+1363858minor)pagefaults 0swaps
[trusty-testing] Starting tests...
Found a test script: ./requirements/container-tests-provider-snappy
[trusty-testing] container-tests-provider-snappy: FAIL
[trusty-testing] stdout: https://paste.ubuntu.com/25024433/
[trusty-testing] stderr: https://paste.ubuntu.com/25024434/
[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+69816outputs (0major+36174minor)pagefaults 0swaps
[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+5290minor)pagefaults 0swaps
[xenial] provisioning container
[xenial] (timing) 70.78user 19.13system 2:14.92elapsed 66%CPU (0avgtext+0avgdata 107732maxresident)k
[xenial] (timing) 24inputs+2564832outputs (2major+2357284minor)pagefaults 0swaps
[xenial-testing] Starting tests...
Found a test script: ./requirements/container-tests-provider-snappy
[xenial-testing] container-tests-provider-snappy: FAIL
[xenial-testing] stdout: https://paste.ubuntu.com/25024441/
[xenial-testing] stderr: https://paste.ubuntu.com/25024442/
[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+69816outputs (0major+37446minor)pagefaults 0swaps
[xenial-testing] Fixing file permissions in source directory
[xenial-testing] Destroying container
Name: xenial-testing
State: STOPPED

review: Needs Fixing
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

rebeased

review: Approve
Revision history for this message
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+5437minor)pagefaults 0swaps
[trusty] provisioning container
[trusty] (timing) 37.17user 14.38system 1:26.54elapsed 59%CPU (0avgtext+0avgdata 67792maxresident)k
[trusty] (timing) 0inputs+1616616outputs (16major+1363306minor)pagefaults 0swaps
[trusty-testing] Starting tests...
Found a test script: ./requirements/container-tests-provider-snappy
[trusty-testing] container-tests-provider-snappy: FAIL
[trusty-testing] stdout: https://paste.ubuntu.com/25024484/
[trusty-testing] stderr: https://paste.ubuntu.com/25024485/
[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+69784outputs (0major+37436minor)pagefaults 0swaps
[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+5232minor)pagefaults 0swaps
[xenial] provisioning container
[xenial] (timing) 71.56user 19.38system 2:14.77elapsed 67%CPU (0avgtext+0avgdata 107732maxresident)k
[xenial] (timing) 24inputs+2564832outputs (2major+2359067minor)pagefaults 0swaps
[xenial-testing] Starting tests...
Found a test script: ./requirements/container-tests-provider-snappy
[xenial-testing] container-tests-provider-snappy: FAIL
[xenial-testing] stdout: https://paste.ubuntu.com/25024494/
[xenial-testing] stderr: https://paste.ubuntu.com/25024495/
[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+69784outputs (0major+37987minor)pagefaults 0swaps
[xenial-testing] Fixing file permissions in source directory
[xenial-testing] Destroying container
Name: xenial-testing
State: STOPPED

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/bin/ansi_parser b/bin/ansi_parser
2deleted file mode 100755
3index 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:]))
167diff --git a/bin/bt_helper.py b/bin/bt_helper.py
168deleted file mode 100644
169index 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)
464diff --git a/bin/cpu_offlining b/bin/cpu_offlining
465deleted file mode 100755
466index 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
516diff --git a/bin/cpu_topology b/bin/cpu_topology
517deleted file mode 100755
518index 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())
637diff --git a/bin/disk_info b/bin/disk_info
638deleted file mode 100755
639index 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())
724diff --git a/bin/disk_read_performance_test b/bin/disk_read_performance_test
725deleted file mode 100755
726index 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
804diff --git a/bin/disk_stats_test b/bin/disk_stats_test
805deleted file mode 100755
806index 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
878diff --git a/bin/disk_stress_ng b/bin/disk_stress_ng
879deleted file mode 100755
880index 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
1126diff --git a/bin/fwts_test b/bin/fwts_test
1127deleted file mode 100755
1128index 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())
1624diff --git a/bin/gateway_ping_test b/bin/gateway_ping_test
1625deleted file mode 100755
1626index 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:]))
1916diff --git a/bin/lsmod_info b/bin/lsmod_info
1917deleted file mode 100755
1918index 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())
1962diff --git a/bin/memory_compare b/bin/memory_compare
1963deleted file mode 100755
1964index 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())
2106diff --git a/bin/memory_test b/bin/memory_test
2107deleted file mode 100755
2108index 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:]))
2351diff --git a/bin/network_device_info b/bin/network_device_info
2352deleted file mode 100755
2353index 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:]))
2623diff --git a/bin/network_info b/bin/network_info
2624deleted file mode 100755
2625index 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:]))
2710diff --git a/bin/network_reconnect_resume_test b/bin/network_reconnect_resume_test
2711deleted file mode 100755
2712index 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())
2818diff --git a/bin/removable_storage_test b/bin/removable_storage_test
2819deleted file mode 100755
2820index 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())
3694diff --git a/bin/removable_storage_watcher b/bin/removable_storage_watcher
3695deleted file mode 100755
3696index 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())
4601diff --git a/bin/sleep_test_log_check b/bin/sleep_test_log_check
4602deleted file mode 100755
4603index 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())
4808diff --git a/bin/storage_test b/bin/storage_test
4809deleted file mode 100755
4810index 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
4908diff --git a/bin/test_bt_keyboard b/bin/test_bt_keyboard
4909index 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():
4921diff --git a/bin/xml_sanitize b/bin/xml_sanitize
4922deleted file mode 100755
4923index 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)
4973diff --git a/manage.py b/manage.py
4974index 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."""
4999diff --git a/requirements/container-tests-provider-snappy b/requirements/container-tests-provider-snappy
5000index 97b8b5f..d0b77e7 100755
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches