Merge lp:~shawn111/checkbox/hexr-dkms into lp:checkbox
- hexr-dkms
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Zygmunt Krynicki |
Approved revision: | 3728 |
Merged at revision: | 3778 |
Proposed branch: | lp:~shawn111/checkbox/hexr-dkms |
Merge into: | lp:checkbox |
Diff against target: |
688 lines (+603/-0) 8 files modified
providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist (+2/-0) providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist (+2/-0) providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist (+2/-0) providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist (+2/-0) providers/plainbox-provider-checkbox/bin/dkms_info (+569/-0) providers/plainbox-provider-checkbox/jobs/info.txt.in (+22/-0) providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist (+2/-0) providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist (+2/-0) |
To merge this branch: | bzr merge lp:~shawn111/checkbox/hexr-dkms |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Zygmunt Krynicki (community) | Approve | ||
Daniel Manrique (community) | Needs Fixing | ||
Review via email: mp+257533@code.launchpad.net |
Commit message
Description of the change
providers:checkbox: Added dkms_info_
$ providers/
workaround-
- oem-audio-
$ providers/
[
{
"arch": "x86_64",
"dkms_ver": "0.1",
"mods": [
"hello"
],
},
{
"arch": "x86_64",
"dkms_ver": "0.8",
]
},
"mods": [
],
"package": {
},
}
]
Zygmunt Krynicki (zyga) wrote : | # |
Lots of details below. Thanks for proposing this but please keep iterating until it all gets fixed.
Also, rewrite your commit messages:
<component>
\n
Longer description as to what is being changed and why. Include as much detail as you want here.
Thanks
ZK
Shawn Wang (shawn111) wrote : | # |
@ZK,
I still don't understand how to use flake8-docstrings to check.
It gave me some Error but I have no idea how to fix, I will do it tomorrow.
* I don't use python3-debian, because I just need the Modaliases information, rfc822 is good for this case
* when will check_output() raise OSError? >> when dkms is not found
Zygmunt Krynicki (zyga) wrote : | # |
To use flake8-docstrings just pip3 install --user flake8-docstrings
and run flake8 on the file you are working on.
The python3-debian module seems to contain the deb822 module which
seems to do the parsing for the Debian variant of RFC822. I would
prefer to use that over the email module as it probably handles
something that the email parser will misinterpret.
Thanks
ZK
On Tue, Apr 28, 2015 at 4:37 PM, Shawn Wang <email address hidden> wrote:
> @ZK,
>
> I still don't understand how to use flake8-docstrings to check.
> It gave me some Error but I have no idea how to fix, I will do it tomorrow.
>
> * I don't use python3-debian, because I just need the Modaliases information, rfc822 is good for this case
> * when will check_output() raise OSError? >> when dkms is not found
>
> --
> https:/
> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
Daniel Manrique (roadmr) wrote : | # |
On Tue, Apr 28, 2015 at 10:42 AM, Zygmunt Krynicki
<email address hidden> wrote:
> To use flake8-docstrings just pip3 install --user flake8-docstrings
> and run flake8 on the file you are working on.
>
> The python3-debian module seems to contain the deb822 module which
> seems to do the parsing for the Debian variant of RFC822. I would
> prefer to use that over the email module as it probably handles
> something that the email parser will misinterpret.
If we use this, will we also need to add python3-debian as a
dependency in packaging?
Just asking/pointing out so we're aware of the possible extra work needed
>
> Thanks
> ZK
>
> On Tue, Apr 28, 2015 at 4:37 PM, Shawn Wang <email address hidden> wrote:
>> @ZK,
>>
>> I still don't understand how to use flake8-docstrings to check.
>> It gave me some Error but I have no idea how to fix, I will do it tomorrow.
>>
>> * I don't use python3-debian, because I just need the Modaliases information, rfc822 is good for this case
>> * when will check_output() raise OSError? >> when dkms is not found
>>
>> --
>> https:/
>> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
>
> --
> https:/
> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
Zygmunt Krynicki (zyga) wrote : | # |
Yes. I was going to wait until Shawn is done with the current batch.
Shawn, please add this next to the new script (in some .pxu file):
# The dkms_info script requires python3-debian package
unit: packaging meta-data
os-id: debian
Depends: python3-debian
For details check the plainbox-
Thanks
ZK
On Tue, Apr 28, 2015 at 5:21 PM, Daniel Manrique
<email address hidden> wrote:
> On Tue, Apr 28, 2015 at 10:42 AM, Zygmunt Krynicki
> <email address hidden> wrote:
>> To use flake8-docstrings just pip3 install --user flake8-docstrings
>> and run flake8 on the file you are working on.
>>
>> The python3-debian module seems to contain the deb822 module which
>> seems to do the parsing for the Debian variant of RFC822. I would
>> prefer to use that over the email module as it probably handles
>> something that the email parser will misinterpret.
>
> If we use this, will we also need to add python3-debian as a
> dependency in packaging?
>
> Just asking/pointing out so we're aware of the possible extra work needed
>
>>
>> Thanks
>> ZK
>>
>> On Tue, Apr 28, 2015 at 4:37 PM, Shawn Wang <email address hidden> wrote:
>>> @ZK,
>>>
>>> I still don't understand how to use flake8-docstrings to check.
>>> It gave me some Error but I have no idea how to fix, I will do it tomorrow.
>>>
>>> * I don't use python3-debian, because I just need the Modaliases information, rfc822 is good for this case
>>> * when will check_output() raise OSError? >> when dkms is not found
>>>
>>> --
>>> https:/
>>> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
>>
>> --
>> https:/
>> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
>
> --
> https:/
> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
Zygmunt Krynicki (zyga) wrote : | # |
It keeps getting better but there are a few more things to fix below.
Zygmunt Krynicki (zyga) wrote : | # |
Hey.
Thanks for all the improvements! There are some more stylistic below, please have a look at them
In general you can cut some of the code away by using @functools.
A "proper docstring looks like this"
def foo(a, b):
"""Do something very simple."""
return a + b
def bar(a, b):
"""
Do something more complex to warrant a longer docstring.
:param a:
The first argument
:param b:
The second argument
:returns:
The phase of the moon
This function computes the phase of the moon, as visible on the *a*th
day of the *b*th month of the current year. If the current year is a
Leap year then the constant "rabbit" is returned instead.
"""
This is somewhat more verbose but it is, in general, easier to read. Use your gut feeling to pick the short vs the long format.
I didn't try running this yet. I'll post a separate set of comments for that later today.
Shawn Wang (shawn111) wrote : | # |
> Hey.
>
> Thanks for all the improvements! There are some more stylistic below, please
> have a look at them
>
> In general you can cut some of the code away by using @functools.
> for many of the expensive things you compute in properties and manually store
> to avoid re-computing them later. The second common thing is the way
> function/method docstrings are formatted.
>
> A "proper docstring looks like this"
>
>
> def foo(a, b):
> """Do something very simple."""
> return a + b
>
>
> def bar(a, b):
> """
> Do something more complex to warrant a longer docstring.
>
> :param a:
> The first argument
> :param b:
> The second argument
> :returns:
> The phase of the moon
>
> This function computes the phase of the moon, as visible on the *a*th
> day of the *b*th month of the current year. If the current year is a
> Leap year then the constant "rabbit" is returned instead.
> """
>
> This is somewhat more verbose but it is, in general, easier to read. Use your
> gut feeling to pick the short vs the long format.
>
> I didn't try running this yet. I'll post a separate set of comments for that
> later today.
Hi Zyga,
I did some performance improve and update the docstring.
However, "dpkg -S", "modalaises matching" are still need some time.
real 0m1.345s (before) -> 0m0.986s
user 0m0.973s (before) -> 0m0.676s
sys 0m0.072s
Zygmunt Krynicki (zyga) wrote : | # |
Please review the comments on the previous iteration of the proposal. You didn't address all of them (lru and uname, for example).
Zygmunt Krynicki (zyga) wrote : | # |
16:25 < roadmr> zyga: got time for another quick look at https:/
16:25 <@zyga> roadmr: yeah, looking
16:26 <@zyga> roadmr: oh, I did today I think
16:26 <@zyga> roadmr: let's see
16:28 <@zyga> roadmr: I did review it but not everything is done yet, I know shawnwang didn't know how to use lru_cache but we discussed that on IRC
16:28 <@zyga> roadmr: I'm waiting for another upload that will address all the manual caching
16:28 < roadmr> zyga: ok, not a problem, thanks!
Shawn Wang (shawn111) wrote : | # |
@Zyga,
I updated with lru_cache and classmethod.
It didn't improve too much of performance.
There are two bottlenecks: dkms status(run one time), match_patterns(run many times).
Both two bottlenecks are depends on how many device packages are installed.
More packages are more slow.
I run on one Dell machine, it is ~22 secs. (6 packages)
another HP machine, it is ~3 secs. (5 packages)
On my own machine, it is ~2 secs. (3 packages)
Zygmunt Krynicki (zyga) wrote : | # |
I worked on this branch today and the results can be seen in the hexr-dkms branch of my git repository [1]
[1] https:/
There are numerous changes that I've made. I think that docstrings and tests are badly missing in a few spots. The user interface could use a few changes too.
I ran the code on my machine with virtualbox module. I could use test data from systems that have the other useful types of module information (oem-something and patterned modaliases).
Lastly I think the way we collect and present information should change. Dicts are great but python objects are dicts too and they feel more suited for the job. This affects the get_dkmses and get_nondkmses functions.
I've left lots of XXX: notes in the tree, on stuff that I didn't finish fixing. Let's see what we can do about this.
Zygmunt Krynicki (zyga) wrote : | # |
NOTE: more useful link to my branch: https:/
- 3727. By Shawn Wang
-
providers:checkbox: add dkms_info to fetch device related package information.
supported package types:
- dkms (Dynamic Kernel Module Support): provides kernel modules
- non-dkms: packages that with modaliases header and don't exist in dkms list
- hardware modalias: might be a dkms or config package
- oemalias: It is like non-dkms(w/o modalias)supported output format:
- summary: one line per packages with matched modaliases information
- json: json output (fully information) - 3728. By Shawn Wang
-
providers:checkbox: add dkms_info_
attachment, device_ package_ info_attachment jobs Collect device related package information,
dkms_info_attachment: json dump format of dkms package information
device_package_ info_attachment : list packages with matched hardware modaliases information
Zygmunt Krynicki (zyga) wrote : | # |
Let's merge it.
Thanks a *lot* for iterating and for changing this so many times. I think we can iron out any remaining issue, -- if any -- as bugs.
Preview Diff
1 | === modified file 'providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist' |
2 | --- providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist 2015-04-24 20:58:54 +0000 |
3 | +++ providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist 2015-05-14 11:56:31 +0000 |
4 | @@ -130,6 +130,8 @@ |
5 | sysfs_attachment |
6 | udev_attachment |
7 | lsmod_attachment |
8 | +device_package_info_attachment |
9 | +dkms_info_attachment |
10 | acpi_sleep_attachment |
11 | info/touchpad_driver |
12 | info/disk_partitions |
13 | |
14 | === modified file 'providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist' |
15 | --- providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist 2015-05-14 06:40:17 +0000 |
16 | +++ providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist 2015-05-14 11:56:31 +0000 |
17 | @@ -412,6 +412,8 @@ |
18 | sysfs_attachment |
19 | udev_attachment |
20 | lsmod_attachment |
21 | +device_package_info_attachment |
22 | +dkms_info_attachment |
23 | acpi_sleep_attachment |
24 | info/hdparm |
25 | info/hdparm_.*.txt |
26 | |
27 | === modified file 'providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist' |
28 | --- providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist 2015-04-29 13:45:16 +0000 |
29 | +++ providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist 2015-05-14 11:56:31 +0000 |
30 | @@ -360,6 +360,8 @@ |
31 | sysfs_attachment |
32 | udev_attachment |
33 | lsmod_attachment |
34 | +device_package_info_attachment |
35 | +dkms_info_attachment |
36 | acpi_sleep_attachment |
37 | info/hdparm |
38 | info/hdparm_.*.txt |
39 | |
40 | === modified file 'providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist' |
41 | --- providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist 2015-04-29 13:45:16 +0000 |
42 | +++ providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist 2015-05-14 11:56:31 +0000 |
43 | @@ -360,6 +360,8 @@ |
44 | sysfs_attachment |
45 | udev_attachment |
46 | lsmod_attachment |
47 | +device_package_info_attachment |
48 | +dkms_info_attachment |
49 | acpi_sleep_attachment |
50 | info/hdparm |
51 | info/hdparm_.*.txt |
52 | |
53 | === added file 'providers/plainbox-provider-checkbox/bin/dkms_info' |
54 | --- providers/plainbox-provider-checkbox/bin/dkms_info 1970-01-01 00:00:00 +0000 |
55 | +++ providers/plainbox-provider-checkbox/bin/dkms_info 2015-05-14 11:56:31 +0000 |
56 | @@ -0,0 +1,569 @@ |
57 | +#!/usr/bin/env python3 |
58 | +# encoding: utf-8 |
59 | +# Copyright 2015 Canonical Ltd. |
60 | +# Written by: |
61 | +# Shawn Wang <shawn.wang@canonical.com> |
62 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
63 | +# |
64 | +# This program is free software: you can redistribute it and/or modify |
65 | +# it under the terms of the GNU General Public License version 3, |
66 | +# as published by the Free Software Foundation. |
67 | +# |
68 | +# This program is distributed in the hope that it will be useful, |
69 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
70 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
71 | +# GNU General Public License for more details. |
72 | +# |
73 | +# You should have received a copy of the GNU General Public License |
74 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
75 | + |
76 | +""" |
77 | +dkms_info provides device package information. |
78 | + |
79 | +supported package types: |
80 | + - dkms (Dynamic Kernel Module Support): provides kernel modules |
81 | + - non-dkms: packages that with modaliases header and don't exist in dkms list |
82 | + - hardware modalias: might be unused dkms or config package |
83 | + - oemalias: It is like non-dkms(w/o modalias) |
84 | +supported output format: |
85 | + - onelines: one line per packages with matched modaliases information |
86 | + - dumps: json output (fully information) |
87 | +""" |
88 | + |
89 | +import fnmatch |
90 | +import functools |
91 | +import email.parser |
92 | +import io |
93 | +import json |
94 | +import logging |
95 | +import os |
96 | +import subprocess |
97 | +import sys |
98 | +import unittest |
99 | + |
100 | +from guacamole import Command |
101 | + |
102 | +try: |
103 | + from unittest import mock |
104 | +except ImportError: |
105 | + from plainbox.vendor import mock |
106 | + |
107 | +_logger = logging.getLogger(None) |
108 | + |
109 | + |
110 | +@functools.lru_cache() |
111 | +def get_system_module_list(): |
112 | + """ |
113 | + Use lsmod to list current kernel modules. |
114 | + |
115 | + :returns: |
116 | + A list of module names that are loaded into the current kernel. |
117 | + """ |
118 | + _logger.info("Looking at inserted kernel modules") |
119 | + modules = [] |
120 | + with io.open("/proc/modules", 'rt', |
121 | + encoding='UTF-8') as stream: |
122 | + for line in stream.readlines(): |
123 | + modules.append(line.split()[0].strip()) |
124 | + return modules |
125 | + |
126 | + |
127 | +@functools.lru_cache() |
128 | +def get_system_modaliases(): |
129 | + r""" |
130 | + List machine modaliases. |
131 | + |
132 | + :returns: |
133 | + dict of hardware modaliases. |
134 | + key: modalias_type |
135 | + value: list of modalias_string |
136 | + """ |
137 | + _logger.info("Looking for modalias files in /sys/devices") |
138 | + |
139 | + result = {} |
140 | + name = "modalias" |
141 | + for root, dirs, files in os.walk("/sys/devices/"): |
142 | + if name in files: |
143 | + with io.open(os.path.join(root, name), 'rt', |
144 | + encoding='UTF-8') as stream: |
145 | + data = stream.read().strip() |
146 | + (modalias_type, modalias_string) = data.split(":", 1) |
147 | + |
148 | + if modalias_type not in result: |
149 | + result[modalias_type] = [] |
150 | + if modalias_string not in result[modalias_type]: |
151 | + result[modalias_type].append(modalias_string) |
152 | + return result |
153 | + |
154 | + |
155 | +def get_installed_dkms_modules(): |
156 | + """ |
157 | + Query dkms_status from /var/lib/dkms/. |
158 | + |
159 | + An installed dkms module has the below directory. |
160 | + /var/lib/dkms/<dkms_name>/<dkms_ver>/<kernel_ver> |
161 | + |
162 | + :returns: |
163 | + list of (<dkms_name>, <dkms_ver>) |
164 | + """ |
165 | + _dkmses = [] |
166 | + _logger.info("Querying dkms database") |
167 | + |
168 | + path = "/var/lib/dkms/" |
169 | + for root, dirs, files in os.walk(path): |
170 | + if os.uname().release in dirs: |
171 | + dkms = root[len(path):].split("/") |
172 | + if len(dkms) != 2: |
173 | + continue |
174 | + _dkmses.append(dkms) |
175 | + return _dkmses |
176 | + |
177 | + |
178 | +@functools.lru_cache() |
179 | +def match_patterns(patterns): |
180 | + """ |
181 | + Check modalias patterns matched with modalias, or type is oemalias. |
182 | + |
183 | + oemalias is a special pattern_type for oem. |
184 | + :param patterns: |
185 | + list of modalias pattern from debian package. |
186 | + :returns: |
187 | + list of modalias pattern matched with hardware modalias |
188 | + """ |
189 | + _logger.info("Looking for modalias objects matching") |
190 | + matched = [] |
191 | + if not patterns: |
192 | + return matched |
193 | + hw_modaliases = get_system_modaliases() |
194 | + for pattern in patterns: |
195 | + pattern_array = pattern.split(":", 1) |
196 | + if len(pattern_array) < 2: |
197 | + _logger.info("skip pattern {}, can't find type".format(pattern)) |
198 | + continue |
199 | + |
200 | + (pattern_type, pattern_string) = pattern_array |
201 | + |
202 | + if pattern_type == "oemalias": |
203 | + matched.append(pattern) |
204 | + if pattern_type not in hw_modaliases: |
205 | + continue |
206 | + for item in hw_modaliases[pattern_type]: |
207 | + if fnmatch.fnmatch(item, pattern_string): |
208 | + matched.append(pattern) |
209 | + return matched |
210 | + |
211 | + |
212 | +class SystemInfoTests(unittest.TestCase): |
213 | + |
214 | + """Tests for System Information Parsing and Collection.""" |
215 | + |
216 | + _proc_modules = """\ |
217 | +xt_REDIRECT 16384 3 - Live 0x0000000000000000 |
218 | +nf_nat_redirect 16384 1 xt_REDIRECT, Live 0x0000000000000000 |
219 | +xt_hl 16384 3 - Live 0x0000000000000000 |
220 | +hid_generic 16384 0 - Live 0x0000000000000000 |
221 | +usbhid 53248 0 - Live 0x0000000000000000 |
222 | +hid 110592 2 hid_generic,usbhid, Live 0x0000000000000000 |
223 | +overlay 45056 1 - Live 0x0000000000000000 |
224 | +""" |
225 | + _modalias = """\ |
226 | +usb:v1D6Bp0003d0319dc09dsc00dp03ic09isc00ip00in00 |
227 | +""" |
228 | + |
229 | + def setUp(self): |
230 | + """Common setup code.""" |
231 | + get_system_module_list.cache_clear() |
232 | + get_system_modaliases.cache_clear() |
233 | + |
234 | + @mock.patch('io.open', mock.mock_open(read_data=_proc_modules)) |
235 | + def test_get_module_list__calls_and_parses_lsmod(self): |
236 | + """Ensure that get_module_list() parses lsmod output.""" |
237 | + # NOTE: Return value was loaded from my system running kernel 4.0. |
238 | + # The first few and last rows to be precise. |
239 | + modules = get_system_module_list() |
240 | + self.assertEqual(modules, [ |
241 | + 'xt_REDIRECT', 'nf_nat_redirect', 'xt_hl', 'hid_generic', |
242 | + 'usbhid', 'hid', 'overlay']) |
243 | + |
244 | + @mock.patch('io.open', mock.mock_open(read_data=_proc_modules)) |
245 | + def test_get_module_list_is_cached(self): |
246 | + """Ensure that get_module_list() cache works.""" |
247 | + modules1 = get_system_module_list() |
248 | + modules2 = get_system_module_list() |
249 | + self.assertIn('xt_REDIRECT', modules1) |
250 | + self.assertIn('overlay', modules2) |
251 | + self.assertEqual(modules1, modules2) |
252 | + |
253 | + @mock.patch('os.walk') |
254 | + @mock.patch('io.open', mock.mock_open(read_data=_modalias)) |
255 | + def test_get_system_modalias(self, mock_os_walk): |
256 | + """test_get_system_modalias.""" |
257 | + mock_os_walk.return_value = [ |
258 | + ("/sys/devices/pci0000:00/0000:00:14.0/usb2/2-0:1.0/modalias", |
259 | + ["driver", "subsystem"], |
260 | + ["modalias", "uevent"]), |
261 | + ] |
262 | + |
263 | + """fetch hw_modaliases from machine and check modalis types.""" |
264 | + modaliases = get_system_modaliases() |
265 | + self.assertEqual(len(modaliases), 1) |
266 | + self.assertIn("usb", modaliases) |
267 | + |
268 | + @mock.patch('os.uname') |
269 | + @mock.patch('os.walk') |
270 | + def test_get_installed_dkms_modules(self, mock_os_walk, mock_os_uname): |
271 | + """test_get_installed_dkms_modules.""" |
272 | + mock_os_walk.return_value = [ |
273 | + ("/var/lib/dkms/hello/0.1", |
274 | + ["3.19.0-15-generic", "build", "source"], |
275 | + []), |
276 | + ] |
277 | + o = mock.Mock() |
278 | + o.release = "3.19.0-15-generic" |
279 | + mock_os_uname.return_value = o |
280 | + self.assertEqual([['hello', '0.1']], |
281 | + get_installed_dkms_modules()) |
282 | + |
283 | + @mock.patch('__main__.get_system_modaliases') |
284 | + def test_match_patterns(self, mock_get_system_modaliases): |
285 | + """Test of match_patterns.""" |
286 | + mock_get_system_modaliases.return_value = { |
287 | + "pci": ["v0000168Cd00000036sv0000103Csd0000217Fbc02sc80i00", |
288 | + "v00008086d00008C26sv0000103Csd000022D9bc0Csc03i20"], |
289 | + "usb": ["v8087p8000d0005dc09dsc00dp01ic09isc00ip00in00", |
290 | + "v1D6Bp0002d0319dc09dsc00dp00ic09isc00ip00in00"], |
291 | + } |
292 | + pkg_modalieses = ["pci:v00008086d00008C26sv*sd*bc*sc*i*", |
293 | + "usb:v07B4p010Ad0102dc*dsc*dp*ic*isc*ip*in*", |
294 | + "oemalias:test"] |
295 | + matched_modalieses = match_patterns(tuple(pkg_modalieses)) |
296 | + # match_patterns |
297 | + self.assertIn("pci:v00008086d00008C26sv*sd*bc*sc*i*", |
298 | + matched_modalieses) |
299 | + self.assertIn("oemalias:test", |
300 | + matched_modalieses) |
301 | + self.assertNotIn("usb:v07B4p010Ad0102dc*dsc*dp*ic*isc*ip*in*", |
302 | + matched_modalieses) |
303 | + |
304 | + |
305 | +class DkmsPackage(object): |
306 | + |
307 | + """ |
308 | + Handle DKMS type device package, DKMS is a kernel module framework. |
309 | + |
310 | + It generate modules for installed kernel or different kernel versions. |
311 | + The dkms modules will be copied to /lib/modulesa/`uname -r`/updates/dkms/. |
312 | + Those modules might be load by modaliases information. |
313 | + """ |
314 | + |
315 | + def __init__(self, name, version): |
316 | + """ |
317 | + init of DkmsPackage, define all the attribute. |
318 | + |
319 | + :param name: |
320 | + DKMS module name |
321 | + :param version: |
322 | + DKMS module version |
323 | + """ |
324 | + self.dkms_name = name |
325 | + self.dkms_ver = version |
326 | + self.pkg_name = self._query_package() |
327 | + self.kernel_ver = os.uname().release |
328 | + self.arch = os.uname().machine |
329 | + self.mods = self._list_modules() |
330 | + self.install_mods = self._list_install_modules() |
331 | + self.pkg = None |
332 | + |
333 | + def _query_package(self): |
334 | + """ |
335 | + Query debian package of dkms. |
336 | + |
337 | + Use dpkg -S to check dkms src path of debian package. |
338 | + |
339 | + :return: |
340 | + string of package name or None |
341 | + """ |
342 | + path = "/usr/src/{}-{}/dkms.conf".format(self.dkms_name, self.dkms_ver) |
343 | + _logger.info("Looking for packages that provide: %s", path) |
344 | + dpkg_info_root = "/var/lib/dpkg/info" |
345 | + for fn in os.listdir(dpkg_info_root): |
346 | + if not fn.endswith(".list"): |
347 | + continue |
348 | + with io.open(os.path.join(dpkg_info_root, fn), 'rt', |
349 | + encoding='UTF-8') as stream: |
350 | + if path in stream.read(): |
351 | + return fn[:-len(".list")] |
352 | + return None |
353 | + |
354 | + def _list_modules(self): |
355 | + """ |
356 | + List all the kernel modules that provide by the dkms package. |
357 | + |
358 | + Module name (.ko) with "-" will be replace to "_" when module loaded. |
359 | + |
360 | + :param path: |
361 | + The directory to look at. |
362 | + :return: |
363 | + List of kernel modules |
364 | + """ |
365 | + path = "/var/lib/dkms/{}/{}/{}/{}/module".format( |
366 | + self.dkms_name, self.dkms_ver, self.kernel_ver, self.arch) |
367 | + _logger.info("Looking for kernel modules in %s", path) |
368 | + result = [] |
369 | + for module_file in os.listdir(path): |
370 | + (module, extension) = os.path.splitext(module_file) |
371 | + if extension == ".ko": |
372 | + result.append(module.replace("-", "_")) |
373 | + return result |
374 | + |
375 | + def _list_install_modules(self): |
376 | + """ |
377 | + Return a dict of install_modules. |
378 | + |
379 | + key is installed module name |
380 | + value is tuple of matched patterns |
381 | + |
382 | + :return: |
383 | + Dict of installed dkms modules |
384 | + """ |
385 | + install_mods = {} |
386 | + for m in self.mods: |
387 | + if m not in get_system_module_list(): |
388 | + continue |
389 | + _logger.info("Inspecting module %s", m) |
390 | + |
391 | + output = subprocess.check_output(["modinfo", m], |
392 | + universal_newlines=True) |
393 | + aliases = [] |
394 | + for line in output.splitlines(): |
395 | + if not line.startswith("alias:"): |
396 | + continue |
397 | + |
398 | + key, value = line.split(':', 1) |
399 | + aliases.append(value.strip()) |
400 | + |
401 | + install_mods[m] = match_patterns(tuple(aliases)) |
402 | + return install_mods |
403 | + |
404 | +def _headers_to_dist(pkg_str): |
405 | + """ |
406 | + Convert rft822 headers string to dict. |
407 | + |
408 | + :param headers: |
409 | + deb822 headers object |
410 | + :return: |
411 | + dict, the key is lowercase of deb822 headers key |
412 | + """ |
413 | + |
414 | + header = email.parser.Parser().parsestr(pkg_str) |
415 | + target = {} |
416 | + for key in header.keys(): |
417 | + target[key.lower()] = header[key] |
418 | + return target |
419 | + |
420 | +class DebianPackageHandler(object): |
421 | + |
422 | + """Use rtf822(email) to handle the package information from file_object.""" |
423 | + |
424 | + def __init__(self, extra_pkgs=[], file_object=None): |
425 | + """ |
426 | + DebianPackageHandler. |
427 | + |
428 | + :param file_object: |
429 | + default file open from /var/lib/dpkg/status, |
430 | + where stored system package information |
431 | + """ |
432 | + if file_object is None: |
433 | + file_object = io.open('/var/lib/dpkg/status', 'rt', |
434 | + encoding='UTF-8') |
435 | + self._file_object = file_object |
436 | + self.extra_pkgs = extra_pkgs |
437 | + self.pkgs = self._get_device_pkgs() |
438 | + |
439 | + def _gen_all_pkg_strs(self): |
440 | + """ |
441 | + Get package information in /var/lib/dpkg/status. |
442 | + |
443 | + :returns: |
444 | + A generator of debian package. |
445 | + """ |
446 | + _logger.info("Loading information about all packages") |
447 | + for pkg_str in self._file_object.read().split('\n\n'): |
448 | + yield pkg_str |
449 | + |
450 | + |
451 | + def _get_device_pkgs(self): |
452 | + """ |
453 | + Only device packages have debian package header 'Modaliases'. |
454 | + |
455 | + This method get packages with the key ``modaliases``. |
456 | + Use the method instead of get_all_pkgs for performance issues. |
457 | + |
458 | + :returns: |
459 | + A list of dict , the dict is converted from debian package header. |
460 | + """ |
461 | + |
462 | + _logger.info("Looking for packages providing modaliases") |
463 | + _modalias_pkgs = {} |
464 | + target_str = "" |
465 | + result = {} |
466 | + |
467 | + for pkg_str in self._gen_all_pkg_strs(): |
468 | + |
469 | + for pkg in self.extra_pkgs: |
470 | + if pkg.pkg_name is None: |
471 | + continue |
472 | + pstr = "Package: {}".format(pkg.pkg_name) |
473 | + if pstr in pkg_str: |
474 | + _logger.info("Gathering information of package, {}".format( |
475 | + pkg.pkg_name)) |
476 | + pkg.pkg = _headers_to_dist(pkg_str) |
477 | + break |
478 | + else: |
479 | + if "Modaliases:" in pkg_str: |
480 | + pkg = _headers_to_dist(pkg_str) |
481 | + |
482 | + (modalias_header, pattern_str) = \ |
483 | + pkg['modaliases'].strip(")").split("(") |
484 | + patterns = pattern_str.split(', ') |
485 | + patterns.sort() |
486 | + pkg['match_patterns'] = match_patterns(tuple(patterns)) |
487 | + |
488 | + with io.open("/var/lib/dpkg/info/{}.list".format(pkg['package']), |
489 | + 'rt', encoding='UTF-8') as stream: |
490 | + if "/dkms.conf" in stream.read(): |
491 | + pkg['unused_dkms'] = True |
492 | + result[pkg['package']] = pkg |
493 | + return result |
494 | + |
495 | + def to_json(self): |
496 | + return json.dumps({ "dkms": self.extra_pkgs, "non-dkms": self.pkgs }, default=lambda o: o.__dict__, sort_keys=True, indent=4) |
497 | + |
498 | + def to_outline(self): |
499 | + result = "" |
500 | + for pkg in self.extra_pkgs: |
501 | + if pkg.pkg is None: |
502 | + continue |
503 | + result = "{}\n{}_{}: {}".format( |
504 | + result, |
505 | + pkg.pkg_name, |
506 | + pkg.pkg["version"], |
507 | + pkg.install_mods) |
508 | + for pkg_name, pkg in self.pkgs.items(): |
509 | + extra_str = "" |
510 | + if "unused_dkms" in pkg: |
511 | + extra_str = "- " |
512 | + result = "{}\n{}{}_{}: {}".format( |
513 | + result, |
514 | + extra_str, |
515 | + pkg_name, |
516 | + pkg["version"], |
517 | + pkg['match_patterns']) |
518 | + return result |
519 | + |
520 | + |
521 | +class DebianPackageHandlerTest(unittest.TestCase): |
522 | + |
523 | + """Test of DebianPackageHandler.""" |
524 | + |
525 | + _var_lib_dpkg_status = """\ |
526 | +Package: foo |
527 | +Status: install ok installed |
528 | +Modaliases: hwe(pci:v000099DDd00000036sv*sd*bc*sc*i*) |
529 | + |
530 | +Package: foo1 |
531 | +Status: install ok installed |
532 | +Modaliases: hwe(pci:v0000AADDd00000036sv*sd*bc*sc*i*) |
533 | + |
534 | +Package: foo2 |
535 | +Status: install ok installed |
536 | + |
537 | +Package: foo3 |
538 | +Status: install ok installed |
539 | + |
540 | +Package: bar |
541 | +Status: install ok installed |
542 | + |
543 | +""" |
544 | + |
545 | + |
546 | + @mock.patch('io.open', mock.mock_open(read_data=_var_lib_dpkg_status)) |
547 | + @mock.patch('__main__.get_system_modaliases') |
548 | + def test_get_pkgs(self, mock_get_system_modaliases): |
549 | + """Test of test_get_pkgs.""" |
550 | + mock_get_system_modaliases.return_value = { |
551 | + "pci": ["v0000168Cd00000036sv0000103Csd0000217Fbc02sc80i00", |
552 | + "v00008086d00008C26sv0000103Csd000022D9bc0Csc03i20"], |
553 | + "usb": ["v8087p8000d0005dc09dsc00dp01ic09isc00ip00in00", |
554 | + "v1D6Bp0002d0319dc09dsc00dp00ic09isc00ip00in00"], |
555 | + } |
556 | + |
557 | + self.pkg_handler = DebianPackageHandler( |
558 | + file_object=io.StringIO(self._var_lib_dpkg_status)) |
559 | + self.assertEqual(len(self.pkg_handler.pkgs), 2) |
560 | + |
561 | + |
562 | +class DeviceInfo(Command): |
563 | + |
564 | + """ |
565 | + Implementation of the dkms-info command. |
566 | + |
567 | + dkms_info provides device package information. |
568 | + |
569 | + @EPILOG@ |
570 | + |
571 | + supported package types: |
572 | + - dkms (Dynamic Kernel Module Support): provides kernel modules |
573 | + - non-dkms: packages that with modaliases header and don't exist in dkms |
574 | + list |
575 | + - hardware modalias: might be unused dkms or config package |
576 | + - oemalias: It is like non-dkms(w/o modalias) |
577 | + |
578 | + supported output formats: |
579 | + - onelines: one line per packages with matched modaliases information |
580 | + - dumps: json output (fully information) |
581 | + """ |
582 | + |
583 | + def register_arguments(self, parser): |
584 | + """Register command line arguments for dkms-info.""" |
585 | + |
586 | + parser.add_argument( |
587 | + '--format', default="onelines", |
588 | + choices=["summary", "json"], |
589 | + help=("Choose output format type: " |
590 | + "summary (one line per packages) " |
591 | + "or json (json format, fully information)")) |
592 | + |
593 | + parser.add_argument( |
594 | + '--output', default=None, |
595 | + help=("Output filename to store the output date")) |
596 | + |
597 | + def invoked(self, ctx): |
598 | + """Invoke dkms-info.""" |
599 | + logging.basicConfig( |
600 | + level=logging.INFO, format='[%(relativeCreated)06dms] %(message)s') |
601 | + _logger.info("Started") |
602 | + |
603 | + dkms_pkgs = [] |
604 | + for (dkms_name, dkms_ver) in get_installed_dkms_modules(): |
605 | + dkms_pkg = DkmsPackage(dkms_name, dkms_ver) |
606 | + dkms_pkgs.append(dkms_pkg) |
607 | + |
608 | + output = sys.stdout |
609 | + if ctx.args.output is not None: |
610 | + output = open(ctx.args.output, 'wt', encoding='UTF-8') |
611 | + |
612 | + pkg_handler = DebianPackageHandler(extra_pkgs=dkms_pkgs) |
613 | + if ctx.args.format == "summary": |
614 | + output.write(pkg_handler.to_outline()) |
615 | + else: |
616 | + output.write(pkg_handler.to_json()) |
617 | + _logger.info("Data collected") |
618 | + |
619 | + |
620 | +if __name__ == '__main__': |
621 | + if '--test' in sys.argv: |
622 | + sys.argv.remove('--test') |
623 | + unittest.main() |
624 | + else: |
625 | + DeviceInfo().main() |
626 | |
627 | === modified file 'providers/plainbox-provider-checkbox/jobs/info.txt.in' |
628 | --- providers/plainbox-provider-checkbox/jobs/info.txt.in 2015-04-29 13:44:43 +0000 |
629 | +++ providers/plainbox-provider-checkbox/jobs/info.txt.in 2015-05-14 11:56:31 +0000 |
630 | @@ -1,3 +1,8 @@ |
631 | +# The dkms_info script requires python3-debian package |
632 | +unit: packaging meta-data |
633 | +os-id: debian |
634 | +Depends: python3-debian |
635 | + |
636 | id: codecs_attachment |
637 | plugin: attachment |
638 | requires: device.driver == 'snd_hda_intel' |
639 | @@ -147,6 +152,23 @@ |
640 | estimated_duration: 0.5 |
641 | _description: Attaches a list of the currently running kernel modules. |
642 | |
643 | +id: device_package_info_attachment |
644 | +plugin: attachment |
645 | +command: dkms_info --format onelines |
646 | +_description: Attaches a list of package with device matched information. |
647 | + included package types: |
648 | + - dkms (Dynamic Kernel Module Support): provides kernel modules |
649 | + - non-dkms: packages that with modaliases header and don't exist in dkms list |
650 | + - hardware modalias: might be unused dkms or config package |
651 | + - oemalias: It is like non-dkms(w/o modalias) |
652 | +_summary: Attaches a list of package with device matched information. |
653 | + |
654 | +id: dkms_info_attachment |
655 | +plugin: attachment |
656 | +command: dkms_info --dkms --format dumps |
657 | +_description: Attaches json dumps of installed dkms package information. |
658 | +_summary: Attaches json dumps of installed dkms package information. |
659 | + |
660 | plugin: attachment |
661 | id: acpi_sleep_attachment |
662 | command: [ -e /proc/acpi/sleep ] && cat /proc/acpi/sleep || echo "No /proc/acpi/sleep found" |
663 | |
664 | === modified file 'providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist' |
665 | --- providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist 2015-04-29 13:45:16 +0000 |
666 | +++ providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist 2015-05-14 11:56:31 +0000 |
667 | @@ -26,6 +26,8 @@ |
668 | sysfs_attachment |
669 | udev_attachment |
670 | lsmod_attachment |
671 | +device_package_info_attachment |
672 | +dkms_info_attachment |
673 | #Automated Test-verification Jobs |
674 | __audio__ |
675 | audio/alsa_record_playback_automated |
676 | |
677 | === modified file 'providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist' |
678 | --- providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist 2015-04-29 13:45:16 +0000 |
679 | +++ providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist 2015-05-14 11:56:31 +0000 |
680 | @@ -17,6 +17,8 @@ |
681 | dmidecode_attachment |
682 | kernel_cmdline_attachment |
683 | lsmod_attachment |
684 | +device_package_info_attachment |
685 | +dkms_info_attachment |
686 | lspci_attachment |
687 | lspci_standard_config_attachment |
688 | modinfo_attachment |
Thanks, the information this gives is very complete. I have a few comments, see below. Also, could you please run flake8 and fix the PEP-8 issues? I found 10 of them but it's probably easier if you run flake8 yourself to see what I mean :)