Merge lp:~shawn111/checkbox/hexr-dkms into lp:checkbox

Proposed by Shawn Wang
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
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Daniel Manrique (community) Needs Fixing
Review via email: mp+257533@code.launchpad.net

Description of the change

providers:checkbox: Added dkms_info_attachment job.

$ providers/plainbox-provider-checkbox/bin/dkms_info --format onelines
workaround-xhci-quirk-trusty-dkms_0.8: {'xhci_quirk': ['pci:v0000168Cd00000036sv*sd*bc*sc*i*']}
- oem-audio-hda-daily-dkms_0.201503121632~ubuntu14.04.1: ['pci:v00008086d*sv*sd*bc04sc03i00*', 'pci:v00008086d*sv*sd*bc04sc03i00*', 'pci:v00008086d00000C0Csv*sd*bc*sc*i*', 'pci:v00008086d00008C20sv*sd*bc*sc*i*']

$ providers/plainbox-provider-checkbox/bin/dkms_info --dkms --format dumps
[
    {
        "arch": "x86_64",
        "dkms_name": "hello",
        "dkms_ver": "0.1",
        "install_mods": {},
        "kernel_ver": "3.19.0-15-generic",
        "mods": [
            "hello"
        ],
        "package_name": null
    },
    {
        "arch": "x86_64",
        "dkms_name": "workaround-xhci-quirk-trusty",
        "dkms_ver": "0.8",
        "install_mods": {
            "xhci_quirk": [
                "pci:v0000168Cd00000036sv*sd*bc*sc*i*"
            ]
        },
        "kernel_ver": "3.19.0-15-generic",
        "mods": [
            "xhci_quirk"
        ],
        "package": {
            "architecture": "all",
            "conffiles": "\n /etc/modprobe.d/xhci-quirk.conf cee794e41804e6e3494a1b8f75a8c99e",
            "depends": "dkms (>= 1.95)",
            "description": "workaround-xhci-quirk-trusty driver in DKMS format.",
            "installed-size": "49",
            "maintainer": "Commercial Engineering <email address hidden>",
            "modaliases": "hwe(pci:v0000168Cd00000036sv*sd*bc*sc*i*, pci:v000014E4d*sv*sd*bc*sc*i*)",
            "package": "workaround-xhci-quirk-trusty-dkms",
            "priority": "optional",
            "section": "misc",
            "status": "install ok installed",
            "version": "0.8"
        },
        "package_name": "workaround-xhci-quirk-trusty-dkms"
    }
]

To post a comment you must log in.
Revision history for this message
Daniel Manrique (roadmr) wrote :

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 :)

review: Needs Fixing
Revision history for this message
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>:<subcomponent>: imperative statement as to what is being changed
\n
Longer description as to what is being changed and why. Include as much detail as you want here.

Thanks
ZK

review: Needs Fixing
Revision history for this message
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

Revision history for this message
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://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533
> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.

Revision history for this message
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://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533
>> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
>
> --
> https://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533
> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.

Revision history for this message
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-packaging-meta-data manual page.

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://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533
>>> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
>>
>> --
>> https://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533
>> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.
>
> --
> https://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533
> You are reviewing the proposed merge of lp:~shawn111/checkbox/hexr-dkms into lp:checkbox.

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

It keeps getting better but there are a few more things to fix below.

review: Needs Fixing
Revision history for this message
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.lrucache() 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.

review: Needs Fixing
Revision history for this message
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.lrucache()
> 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

Revision history for this message
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).

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

16:25 < roadmr> zyga: got time for another quick look at https://code.launchpad.net/~shawn111/checkbox/hexr-dkms/+merge/257533 ? some things may still be missing :(
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!

Revision history for this message
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)

Revision history for this message
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://code.launchpad.net/~zyga/checkbox/+git/checkbox/+ref/hexr-dkms

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.

review: Needs Fixing
Revision history for this message
Zygmunt Krynicki (zyga) wrote :
lp:~shawn111/checkbox/hexr-dkms updated
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

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

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist'
--- providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist 2015-04-24 20:58:54 +0000
+++ providers/plainbox-provider-certification-client/whitelists/client-cert-regression.whitelist 2015-05-14 11:56:31 +0000
@@ -130,6 +130,8 @@
130sysfs_attachment130sysfs_attachment
131udev_attachment131udev_attachment
132lsmod_attachment132lsmod_attachment
133device_package_info_attachment
134dkms_info_attachment
133acpi_sleep_attachment135acpi_sleep_attachment
134info/touchpad_driver136info/touchpad_driver
135info/disk_partitions137info/disk_partitions
136138
=== modified file 'providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist'
--- providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist 2015-05-14 06:40:17 +0000
+++ providers/plainbox-provider-certification-client/whitelists/client-cert.whitelist 2015-05-14 11:56:31 +0000
@@ -412,6 +412,8 @@
412sysfs_attachment412sysfs_attachment
413udev_attachment413udev_attachment
414lsmod_attachment414lsmod_attachment
415device_package_info_attachment
416dkms_info_attachment
415acpi_sleep_attachment417acpi_sleep_attachment
416info/hdparm418info/hdparm
417info/hdparm_.*.txt419info/hdparm_.*.txt
418420
=== modified file 'providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist'
--- providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist 2015-04-29 13:45:16 +0000
+++ providers/plainbox-provider-certification-client/whitelists/client-selftest-14-04.whitelist 2015-05-14 11:56:31 +0000
@@ -360,6 +360,8 @@
360sysfs_attachment360sysfs_attachment
361udev_attachment361udev_attachment
362lsmod_attachment362lsmod_attachment
363device_package_info_attachment
364dkms_info_attachment
363acpi_sleep_attachment365acpi_sleep_attachment
364info/hdparm366info/hdparm
365info/hdparm_.*.txt367info/hdparm_.*.txt
366368
=== modified file 'providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist'
--- providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist 2015-04-29 13:45:16 +0000
+++ providers/plainbox-provider-certification-client/whitelists/client-selftest.whitelist 2015-05-14 11:56:31 +0000
@@ -360,6 +360,8 @@
360sysfs_attachment360sysfs_attachment
361udev_attachment361udev_attachment
362lsmod_attachment362lsmod_attachment
363device_package_info_attachment
364dkms_info_attachment
363acpi_sleep_attachment365acpi_sleep_attachment
364info/hdparm366info/hdparm
365info/hdparm_.*.txt367info/hdparm_.*.txt
366368
=== added file 'providers/plainbox-provider-checkbox/bin/dkms_info'
--- providers/plainbox-provider-checkbox/bin/dkms_info 1970-01-01 00:00:00 +0000
+++ providers/plainbox-provider-checkbox/bin/dkms_info 2015-05-14 11:56:31 +0000
@@ -0,0 +1,569 @@
1#!/usr/bin/env python3
2# encoding: utf-8
3# Copyright 2015 Canonical Ltd.
4# Written by:
5# Shawn Wang <shawn.wang@canonical.com>
6# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 3,
10# as published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20"""
21dkms_info provides device package information.
22
23supported package types:
24 - dkms (Dynamic Kernel Module Support): provides kernel modules
25 - non-dkms: packages that with modaliases header and don't exist in dkms list
26 - hardware modalias: might be unused dkms or config package
27 - oemalias: It is like non-dkms(w/o modalias)
28supported output format:
29 - onelines: one line per packages with matched modaliases information
30 - dumps: json output (fully information)
31"""
32
33import fnmatch
34import functools
35import email.parser
36import io
37import json
38import logging
39import os
40import subprocess
41import sys
42import unittest
43
44from guacamole import Command
45
46try:
47 from unittest import mock
48except ImportError:
49 from plainbox.vendor import mock
50
51_logger = logging.getLogger(None)
52
53
54@functools.lru_cache()
55def get_system_module_list():
56 """
57 Use lsmod to list current kernel modules.
58
59 :returns:
60 A list of module names that are loaded into the current kernel.
61 """
62 _logger.info("Looking at inserted kernel modules")
63 modules = []
64 with io.open("/proc/modules", 'rt',
65 encoding='UTF-8') as stream:
66 for line in stream.readlines():
67 modules.append(line.split()[0].strip())
68 return modules
69
70
71@functools.lru_cache()
72def get_system_modaliases():
73 r"""
74 List machine modaliases.
75
76 :returns:
77 dict of hardware modaliases.
78 key: modalias_type
79 value: list of modalias_string
80 """
81 _logger.info("Looking for modalias files in /sys/devices")
82
83 result = {}
84 name = "modalias"
85 for root, dirs, files in os.walk("/sys/devices/"):
86 if name in files:
87 with io.open(os.path.join(root, name), 'rt',
88 encoding='UTF-8') as stream:
89 data = stream.read().strip()
90 (modalias_type, modalias_string) = data.split(":", 1)
91
92 if modalias_type not in result:
93 result[modalias_type] = []
94 if modalias_string not in result[modalias_type]:
95 result[modalias_type].append(modalias_string)
96 return result
97
98
99def get_installed_dkms_modules():
100 """
101 Query dkms_status from /var/lib/dkms/.
102
103 An installed dkms module has the below directory.
104 /var/lib/dkms/<dkms_name>/<dkms_ver>/<kernel_ver>
105
106 :returns:
107 list of (<dkms_name>, <dkms_ver>)
108 """
109 _dkmses = []
110 _logger.info("Querying dkms database")
111
112 path = "/var/lib/dkms/"
113 for root, dirs, files in os.walk(path):
114 if os.uname().release in dirs:
115 dkms = root[len(path):].split("/")
116 if len(dkms) != 2:
117 continue
118 _dkmses.append(dkms)
119 return _dkmses
120
121
122@functools.lru_cache()
123def match_patterns(patterns):
124 """
125 Check modalias patterns matched with modalias, or type is oemalias.
126
127 oemalias is a special pattern_type for oem.
128 :param patterns:
129 list of modalias pattern from debian package.
130 :returns:
131 list of modalias pattern matched with hardware modalias
132 """
133 _logger.info("Looking for modalias objects matching")
134 matched = []
135 if not patterns:
136 return matched
137 hw_modaliases = get_system_modaliases()
138 for pattern in patterns:
139 pattern_array = pattern.split(":", 1)
140 if len(pattern_array) < 2:
141 _logger.info("skip pattern {}, can't find type".format(pattern))
142 continue
143
144 (pattern_type, pattern_string) = pattern_array
145
146 if pattern_type == "oemalias":
147 matched.append(pattern)
148 if pattern_type not in hw_modaliases:
149 continue
150 for item in hw_modaliases[pattern_type]:
151 if fnmatch.fnmatch(item, pattern_string):
152 matched.append(pattern)
153 return matched
154
155
156class SystemInfoTests(unittest.TestCase):
157
158 """Tests for System Information Parsing and Collection."""
159
160 _proc_modules = """\
161xt_REDIRECT 16384 3 - Live 0x0000000000000000
162nf_nat_redirect 16384 1 xt_REDIRECT, Live 0x0000000000000000
163xt_hl 16384 3 - Live 0x0000000000000000
164hid_generic 16384 0 - Live 0x0000000000000000
165usbhid 53248 0 - Live 0x0000000000000000
166hid 110592 2 hid_generic,usbhid, Live 0x0000000000000000
167overlay 45056 1 - Live 0x0000000000000000
168"""
169 _modalias = """\
170usb:v1D6Bp0003d0319dc09dsc00dp03ic09isc00ip00in00
171"""
172
173 def setUp(self):
174 """Common setup code."""
175 get_system_module_list.cache_clear()
176 get_system_modaliases.cache_clear()
177
178 @mock.patch('io.open', mock.mock_open(read_data=_proc_modules))
179 def test_get_module_list__calls_and_parses_lsmod(self):
180 """Ensure that get_module_list() parses lsmod output."""
181 # NOTE: Return value was loaded from my system running kernel 4.0.
182 # The first few and last rows to be precise.
183 modules = get_system_module_list()
184 self.assertEqual(modules, [
185 'xt_REDIRECT', 'nf_nat_redirect', 'xt_hl', 'hid_generic',
186 'usbhid', 'hid', 'overlay'])
187
188 @mock.patch('io.open', mock.mock_open(read_data=_proc_modules))
189 def test_get_module_list_is_cached(self):
190 """Ensure that get_module_list() cache works."""
191 modules1 = get_system_module_list()
192 modules2 = get_system_module_list()
193 self.assertIn('xt_REDIRECT', modules1)
194 self.assertIn('overlay', modules2)
195 self.assertEqual(modules1, modules2)
196
197 @mock.patch('os.walk')
198 @mock.patch('io.open', mock.mock_open(read_data=_modalias))
199 def test_get_system_modalias(self, mock_os_walk):
200 """test_get_system_modalias."""
201 mock_os_walk.return_value = [
202 ("/sys/devices/pci0000:00/0000:00:14.0/usb2/2-0:1.0/modalias",
203 ["driver", "subsystem"],
204 ["modalias", "uevent"]),
205 ]
206
207 """fetch hw_modaliases from machine and check modalis types."""
208 modaliases = get_system_modaliases()
209 self.assertEqual(len(modaliases), 1)
210 self.assertIn("usb", modaliases)
211
212 @mock.patch('os.uname')
213 @mock.patch('os.walk')
214 def test_get_installed_dkms_modules(self, mock_os_walk, mock_os_uname):
215 """test_get_installed_dkms_modules."""
216 mock_os_walk.return_value = [
217 ("/var/lib/dkms/hello/0.1",
218 ["3.19.0-15-generic", "build", "source"],
219 []),
220 ]
221 o = mock.Mock()
222 o.release = "3.19.0-15-generic"
223 mock_os_uname.return_value = o
224 self.assertEqual([['hello', '0.1']],
225 get_installed_dkms_modules())
226
227 @mock.patch('__main__.get_system_modaliases')
228 def test_match_patterns(self, mock_get_system_modaliases):
229 """Test of match_patterns."""
230 mock_get_system_modaliases.return_value = {
231 "pci": ["v0000168Cd00000036sv0000103Csd0000217Fbc02sc80i00",
232 "v00008086d00008C26sv0000103Csd000022D9bc0Csc03i20"],
233 "usb": ["v8087p8000d0005dc09dsc00dp01ic09isc00ip00in00",
234 "v1D6Bp0002d0319dc09dsc00dp00ic09isc00ip00in00"],
235 }
236 pkg_modalieses = ["pci:v00008086d00008C26sv*sd*bc*sc*i*",
237 "usb:v07B4p010Ad0102dc*dsc*dp*ic*isc*ip*in*",
238 "oemalias:test"]
239 matched_modalieses = match_patterns(tuple(pkg_modalieses))
240 # match_patterns
241 self.assertIn("pci:v00008086d00008C26sv*sd*bc*sc*i*",
242 matched_modalieses)
243 self.assertIn("oemalias:test",
244 matched_modalieses)
245 self.assertNotIn("usb:v07B4p010Ad0102dc*dsc*dp*ic*isc*ip*in*",
246 matched_modalieses)
247
248
249class DkmsPackage(object):
250
251 """
252 Handle DKMS type device package, DKMS is a kernel module framework.
253
254 It generate modules for installed kernel or different kernel versions.
255 The dkms modules will be copied to /lib/modulesa/`uname -r`/updates/dkms/.
256 Those modules might be load by modaliases information.
257 """
258
259 def __init__(self, name, version):
260 """
261 init of DkmsPackage, define all the attribute.
262
263 :param name:
264 DKMS module name
265 :param version:
266 DKMS module version
267 """
268 self.dkms_name = name
269 self.dkms_ver = version
270 self.pkg_name = self._query_package()
271 self.kernel_ver = os.uname().release
272 self.arch = os.uname().machine
273 self.mods = self._list_modules()
274 self.install_mods = self._list_install_modules()
275 self.pkg = None
276
277 def _query_package(self):
278 """
279 Query debian package of dkms.
280
281 Use dpkg -S to check dkms src path of debian package.
282
283 :return:
284 string of package name or None
285 """
286 path = "/usr/src/{}-{}/dkms.conf".format(self.dkms_name, self.dkms_ver)
287 _logger.info("Looking for packages that provide: %s", path)
288 dpkg_info_root = "/var/lib/dpkg/info"
289 for fn in os.listdir(dpkg_info_root):
290 if not fn.endswith(".list"):
291 continue
292 with io.open(os.path.join(dpkg_info_root, fn), 'rt',
293 encoding='UTF-8') as stream:
294 if path in stream.read():
295 return fn[:-len(".list")]
296 return None
297
298 def _list_modules(self):
299 """
300 List all the kernel modules that provide by the dkms package.
301
302 Module name (.ko) with "-" will be replace to "_" when module loaded.
303
304 :param path:
305 The directory to look at.
306 :return:
307 List of kernel modules
308 """
309 path = "/var/lib/dkms/{}/{}/{}/{}/module".format(
310 self.dkms_name, self.dkms_ver, self.kernel_ver, self.arch)
311 _logger.info("Looking for kernel modules in %s", path)
312 result = []
313 for module_file in os.listdir(path):
314 (module, extension) = os.path.splitext(module_file)
315 if extension == ".ko":
316 result.append(module.replace("-", "_"))
317 return result
318
319 def _list_install_modules(self):
320 """
321 Return a dict of install_modules.
322
323 key is installed module name
324 value is tuple of matched patterns
325
326 :return:
327 Dict of installed dkms modules
328 """
329 install_mods = {}
330 for m in self.mods:
331 if m not in get_system_module_list():
332 continue
333 _logger.info("Inspecting module %s", m)
334
335 output = subprocess.check_output(["modinfo", m],
336 universal_newlines=True)
337 aliases = []
338 for line in output.splitlines():
339 if not line.startswith("alias:"):
340 continue
341
342 key, value = line.split(':', 1)
343 aliases.append(value.strip())
344
345 install_mods[m] = match_patterns(tuple(aliases))
346 return install_mods
347
348def _headers_to_dist(pkg_str):
349 """
350 Convert rft822 headers string to dict.
351
352 :param headers:
353 deb822 headers object
354 :return:
355 dict, the key is lowercase of deb822 headers key
356 """
357
358 header = email.parser.Parser().parsestr(pkg_str)
359 target = {}
360 for key in header.keys():
361 target[key.lower()] = header[key]
362 return target
363
364class DebianPackageHandler(object):
365
366 """Use rtf822(email) to handle the package information from file_object."""
367
368 def __init__(self, extra_pkgs=[], file_object=None):
369 """
370 DebianPackageHandler.
371
372 :param file_object:
373 default file open from /var/lib/dpkg/status,
374 where stored system package information
375 """
376 if file_object is None:
377 file_object = io.open('/var/lib/dpkg/status', 'rt',
378 encoding='UTF-8')
379 self._file_object = file_object
380 self.extra_pkgs = extra_pkgs
381 self.pkgs = self._get_device_pkgs()
382
383 def _gen_all_pkg_strs(self):
384 """
385 Get package information in /var/lib/dpkg/status.
386
387 :returns:
388 A generator of debian package.
389 """
390 _logger.info("Loading information about all packages")
391 for pkg_str in self._file_object.read().split('\n\n'):
392 yield pkg_str
393
394
395 def _get_device_pkgs(self):
396 """
397 Only device packages have debian package header 'Modaliases'.
398
399 This method get packages with the key ``modaliases``.
400 Use the method instead of get_all_pkgs for performance issues.
401
402 :returns:
403 A list of dict , the dict is converted from debian package header.
404 """
405
406 _logger.info("Looking for packages providing modaliases")
407 _modalias_pkgs = {}
408 target_str = ""
409 result = {}
410
411 for pkg_str in self._gen_all_pkg_strs():
412
413 for pkg in self.extra_pkgs:
414 if pkg.pkg_name is None:
415 continue
416 pstr = "Package: {}".format(pkg.pkg_name)
417 if pstr in pkg_str:
418 _logger.info("Gathering information of package, {}".format(
419 pkg.pkg_name))
420 pkg.pkg = _headers_to_dist(pkg_str)
421 break
422 else:
423 if "Modaliases:" in pkg_str:
424 pkg = _headers_to_dist(pkg_str)
425
426 (modalias_header, pattern_str) = \
427 pkg['modaliases'].strip(")").split("(")
428 patterns = pattern_str.split(', ')
429 patterns.sort()
430 pkg['match_patterns'] = match_patterns(tuple(patterns))
431
432 with io.open("/var/lib/dpkg/info/{}.list".format(pkg['package']),
433 'rt', encoding='UTF-8') as stream:
434 if "/dkms.conf" in stream.read():
435 pkg['unused_dkms'] = True
436 result[pkg['package']] = pkg
437 return result
438
439 def to_json(self):
440 return json.dumps({ "dkms": self.extra_pkgs, "non-dkms": self.pkgs }, default=lambda o: o.__dict__, sort_keys=True, indent=4)
441
442 def to_outline(self):
443 result = ""
444 for pkg in self.extra_pkgs:
445 if pkg.pkg is None:
446 continue
447 result = "{}\n{}_{}: {}".format(
448 result,
449 pkg.pkg_name,
450 pkg.pkg["version"],
451 pkg.install_mods)
452 for pkg_name, pkg in self.pkgs.items():
453 extra_str = ""
454 if "unused_dkms" in pkg:
455 extra_str = "- "
456 result = "{}\n{}{}_{}: {}".format(
457 result,
458 extra_str,
459 pkg_name,
460 pkg["version"],
461 pkg['match_patterns'])
462 return result
463
464
465class DebianPackageHandlerTest(unittest.TestCase):
466
467 """Test of DebianPackageHandler."""
468
469 _var_lib_dpkg_status = """\
470Package: foo
471Status: install ok installed
472Modaliases: hwe(pci:v000099DDd00000036sv*sd*bc*sc*i*)
473
474Package: foo1
475Status: install ok installed
476Modaliases: hwe(pci:v0000AADDd00000036sv*sd*bc*sc*i*)
477
478Package: foo2
479Status: install ok installed
480
481Package: foo3
482Status: install ok installed
483
484Package: bar
485Status: install ok installed
486
487"""
488
489
490 @mock.patch('io.open', mock.mock_open(read_data=_var_lib_dpkg_status))
491 @mock.patch('__main__.get_system_modaliases')
492 def test_get_pkgs(self, mock_get_system_modaliases):
493 """Test of test_get_pkgs."""
494 mock_get_system_modaliases.return_value = {
495 "pci": ["v0000168Cd00000036sv0000103Csd0000217Fbc02sc80i00",
496 "v00008086d00008C26sv0000103Csd000022D9bc0Csc03i20"],
497 "usb": ["v8087p8000d0005dc09dsc00dp01ic09isc00ip00in00",
498 "v1D6Bp0002d0319dc09dsc00dp00ic09isc00ip00in00"],
499 }
500
501 self.pkg_handler = DebianPackageHandler(
502 file_object=io.StringIO(self._var_lib_dpkg_status))
503 self.assertEqual(len(self.pkg_handler.pkgs), 2)
504
505
506class DeviceInfo(Command):
507
508 """
509 Implementation of the dkms-info command.
510
511 dkms_info provides device package information.
512
513 @EPILOG@
514
515 supported package types:
516 - dkms (Dynamic Kernel Module Support): provides kernel modules
517 - non-dkms: packages that with modaliases header and don't exist in dkms
518 list
519 - hardware modalias: might be unused dkms or config package
520 - oemalias: It is like non-dkms(w/o modalias)
521
522 supported output formats:
523 - onelines: one line per packages with matched modaliases information
524 - dumps: json output (fully information)
525 """
526
527 def register_arguments(self, parser):
528 """Register command line arguments for dkms-info."""
529
530 parser.add_argument(
531 '--format', default="onelines",
532 choices=["summary", "json"],
533 help=("Choose output format type: "
534 "summary (one line per packages) "
535 "or json (json format, fully information)"))
536
537 parser.add_argument(
538 '--output', default=None,
539 help=("Output filename to store the output date"))
540
541 def invoked(self, ctx):
542 """Invoke dkms-info."""
543 logging.basicConfig(
544 level=logging.INFO, format='[%(relativeCreated)06dms] %(message)s')
545 _logger.info("Started")
546
547 dkms_pkgs = []
548 for (dkms_name, dkms_ver) in get_installed_dkms_modules():
549 dkms_pkg = DkmsPackage(dkms_name, dkms_ver)
550 dkms_pkgs.append(dkms_pkg)
551
552 output = sys.stdout
553 if ctx.args.output is not None:
554 output = open(ctx.args.output, 'wt', encoding='UTF-8')
555
556 pkg_handler = DebianPackageHandler(extra_pkgs=dkms_pkgs)
557 if ctx.args.format == "summary":
558 output.write(pkg_handler.to_outline())
559 else:
560 output.write(pkg_handler.to_json())
561 _logger.info("Data collected")
562
563
564if __name__ == '__main__':
565 if '--test' in sys.argv:
566 sys.argv.remove('--test')
567 unittest.main()
568 else:
569 DeviceInfo().main()
0570
=== modified file 'providers/plainbox-provider-checkbox/jobs/info.txt.in'
--- providers/plainbox-provider-checkbox/jobs/info.txt.in 2015-04-29 13:44:43 +0000
+++ providers/plainbox-provider-checkbox/jobs/info.txt.in 2015-05-14 11:56:31 +0000
@@ -1,3 +1,8 @@
1# The dkms_info script requires python3-debian package
2unit: packaging meta-data
3os-id: debian
4Depends: python3-debian
5
1id: codecs_attachment6id: codecs_attachment
2plugin: attachment7plugin: attachment
3requires: device.driver == 'snd_hda_intel'8requires: device.driver == 'snd_hda_intel'
@@ -147,6 +152,23 @@
147estimated_duration: 0.5152estimated_duration: 0.5
148_description: Attaches a list of the currently running kernel modules.153_description: Attaches a list of the currently running kernel modules.
149154
155id: device_package_info_attachment
156plugin: attachment
157command: dkms_info --format onelines
158_description: Attaches a list of package with device matched information.
159 included package types:
160 - dkms (Dynamic Kernel Module Support): provides kernel modules
161 - non-dkms: packages that with modaliases header and don't exist in dkms list
162 - hardware modalias: might be unused dkms or config package
163 - oemalias: It is like non-dkms(w/o modalias)
164_summary: Attaches a list of package with device matched information.
165
166id: dkms_info_attachment
167plugin: attachment
168command: dkms_info --dkms --format dumps
169_description: Attaches json dumps of installed dkms package information.
170_summary: Attaches json dumps of installed dkms package information.
171
150plugin: attachment172plugin: attachment
151id: acpi_sleep_attachment173id: acpi_sleep_attachment
152command: [ -e /proc/acpi/sleep ] && cat /proc/acpi/sleep || echo "No /proc/acpi/sleep found"174command: [ -e /proc/acpi/sleep ] && cat /proc/acpi/sleep || echo "No /proc/acpi/sleep found"
153175
=== modified file 'providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist'
--- providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist 2015-04-29 13:45:16 +0000
+++ providers/plainbox-provider-checkbox/whitelists/autotesting.whitelist 2015-05-14 11:56:31 +0000
@@ -26,6 +26,8 @@
26sysfs_attachment26sysfs_attachment
27udev_attachment27udev_attachment
28lsmod_attachment28lsmod_attachment
29device_package_info_attachment
30dkms_info_attachment
29#Automated Test-verification Jobs31#Automated Test-verification Jobs
30__audio__32__audio__
31audio/alsa_record_playback_automated33audio/alsa_record_playback_automated
3234
=== modified file 'providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist'
--- providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist 2015-04-29 13:45:16 +0000
+++ providers/plainbox-provider-checkbox/whitelists/hwsubmit.whitelist 2015-05-14 11:56:31 +0000
@@ -17,6 +17,8 @@
17dmidecode_attachment17dmidecode_attachment
18kernel_cmdline_attachment18kernel_cmdline_attachment
19lsmod_attachment19lsmod_attachment
20device_package_info_attachment
21dkms_info_attachment
20lspci_attachment22lspci_attachment
21lspci_standard_config_attachment23lspci_standard_config_attachment
22modinfo_attachment24modinfo_attachment

Subscribers

People subscribed via source and target branches