~launchpad-pqm/launchpad/devel

8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
3
5265.2.2 by Abel Deuring
implemented revier's comments
4
"""Parse Hardware Database submissions.
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
5
5265.2.2 by Abel Deuring
implemented revier's comments
6
Base classes, intended to be used both for the commercial certification
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
7
data and for the community test submissions.
8
"""
9
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
10
__metaclass__ = type
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
11
__all__ = [
12
           'SubmissionParser',
13
           'process_pending_submissions',
14
          ]
15
16
import bz2
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
17
from cStringIO import StringIO
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
18
12751.2.1 by j.c.sackett
Reformatted imports.
19
#XXX: Given the version of python we know we're running on now,
20
# is the try/except here necessary?
9328.2.1 by Max Bowsher
Modify all imports of cElementTree to try both the Python 2.5+ and Python 2.4 names for the module.
21
try:
22
    import xml.etree.cElementTree as etree
23
except ImportError:
24
    import cElementTree as etree
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
25
from datetime import datetime, timedelta
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
26
from logging import getLogger
5628.8.2 by Abel Deuring
implemented reviewer's comments
27
import os
28
import re
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
29
import sys
5915.2.1 by Abel Deuring
replace the lxml module by elementtree and by xmllint for RelaxNG valdidation
30
5628.8.2 by Abel Deuring
implemented reviewer's comments
31
import pytz
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
32
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
33
from zope.component import getUtility
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
34
from zope.interface import implements
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
35
6374.15.13 by Barry Warsaw
mergeRF
36
from canonical.lazr.xml import RelaxNGValidator
37
11382.6.33 by Gavin Panella
Convert all remaining uses of canonical.cachedproperty to lp.services.propertycache.
38
from lp.services.propertycache import cachedproperty
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
39
from canonical.config import config
7881.6.4 by Abel Deuring
re-implementation of handling Librarian errors in scripts/hwdbsubmissions.py, based on better error handling in FileDownloadClient.getFileByAlias().
40
from canonical.librarian.interfaces import LibrarianServerError
10234.3.3 by Curtis Hovey
Migrated hardward database to lp. Updated test_doc to run the hwddb test.
41
from lp.hardwaredb.interfaces.hwdb import (
12751.2.1 by j.c.sackett
Reformatted imports.
42
    HWBus,
43
    HWSubmissionProcessingStatus,
44
    IHWDeviceDriverLinkSet,
45
    IHWDeviceSet,
46
    IHWDriverSet,
47
    IHWSubmissionDeviceSet,
48
    IHWSubmissionSet,
49
    IHWVendorIDSet,
50
    IHWVendorNameSet,
51
    )
7508.1.1 by Abel Deuring
fix for bug 314342: HWDB submission processing script does not process private submissions
52
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
53
from canonical.launchpad.interfaces.looptuner import ITunableLoop
54
from canonical.launchpad.utilities.looptuner import LoopTuner
55
from canonical.launchpad.webapp.errorlog import (
12751.2.1 by j.c.sackett
Reformatted imports.
56
    ErrorReportingUtility,
57
    ScriptRequest,
58
    )
12751.2.4 by j.c.sackett
Added a context manager to allow the disabling of oops handling when needed in cronscripts, and updated tests.
59
from lp.services.scripts.base import disable_oops_handler
5265.2.2 by Abel Deuring
implemented revier's comments
60
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
61
_relax_ng_files = {
5265.2.2 by Abel Deuring
implemented revier's comments
62
    '1.0': 'hardware-1_0.rng', }
63
5628.8.2 by Abel Deuring
implemented reviewer's comments
64
_time_regex = re.compile(r"""
65
    ^(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)
66
    T(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)
67
    (?:\.(?P<second_fraction>\d{0,6}))?
68
    (?P<tz>
69
        (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d))
70
        | Z)?$
71
    """,
72
    re.VERBOSE)
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
73
5840.2.2 by Abel Deuring
implemented reviewer's comments
74
ROOT_UDI = '/org/freedesktop/Hal/devices/computer'
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
75
UDEV_ROOT_PATH = '/devices/LNXSYSTM:00'
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
76
8294.5.1 by Abel Deuring
Fix for bug 336613: ignore certain duplicate UDIs in HWDB submissions
77
# These UDIs appears in some submissions more than once.
78
KNOWN_DUPLICATE_UDIS = set((
79
    '/org/freedesktop/Hal/devices/ssb__null_',
80
    '/org/freedesktop/Hal/devices/uinput',
81
    '/org/freedesktop/Hal/devices/ignored-device',
82
    ))
83
6261.2.5 by Abel Deuring
implemented reviewer's commets
84
# See include/linux/pci_ids.h in the Linux kernel sources for a complete
85
# list of PCI class and subclass codes.
6261.2.6 by Abel Deuring
implemented reviewer's comments
86
PCI_CLASS_STORAGE = 1
87
PCI_SUBCLASS_STORAGE_SATA = 6
88
89
PCI_CLASS_BRIDGE = 6
90
PCI_SUBCLASS_BRIDGE_PCI = 4
91
PCI_SUBCLASS_BRIDGE_CARDBUS = 7
92
6261.2.5 by Abel Deuring
implemented reviewer's commets
93
PCI_CLASS_SERIALBUS_CONTROLLER = 12
6261.2.6 by Abel Deuring
implemented reviewer's comments
94
PCI_SUBCLASS_SERIALBUS_USB = 3
5915.2.1 by Abel Deuring
replace the lxml module by elementtree and by xmllint for RelaxNG valdidation
95
6533.1.2 by Abel Deuring
implemented reviewer's comments
96
WARNING_NO_HAL_KERNEL_VERSION = 1
97
WARNING_NO_KERNEL_PACKAGE_DATA = 2
98
99
DB_FORMAT_FOR_VENDOR_ID = {
100
    'pci': '0x%04x',
101
    'usb_device': '0x%04x',
102
    'scsi': '%-8s',
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
103
    'scsi_device': '%-8s',
6533.1.2 by Abel Deuring
implemented reviewer's comments
104
    }
105
106
DB_FORMAT_FOR_PRODUCT_ID = {
107
    'pci': '0x%04x',
108
    'usb_device': '0x%04x',
109
    'scsi': '%-16s',
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
110
    'scsi_device': '%-16s',
6533.1.2 by Abel Deuring
implemented reviewer's comments
111
    }
112
9644.3.8 by Abel Deuring
implemented reviewer's comments
113
UDEV_USB_DEVICE_PROPERTIES = set(('DEVTYPE', 'PRODUCT', 'TYPE'))
114
UDEV_USB_PRODUCT_RE = re.compile(
115
    '^[0-9a-f]{1,4}/[0-9a-f]{1,4}/[0-9a-f]{1,4}$', re.I)
116
UDEV_USB_TYPE_RE = re.compile('^[0-9]{1,3}/[0-9]{1,3}/[0-9]{1,3}$')
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
117
SYSFS_SCSI_DEVICE_ATTRIBUTES = set(('vendor', 'model', 'type'))
9644.3.8 by Abel Deuring
implemented reviewer's comments
118
7184.4.4 by Abel Deuring
moved the definition of test classes derived from SubmissionParser from test methods to to top-level
119
class SubmissionParser(object):
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
120
    """A Parser for the submissions to the hardware database."""
121
122
    def __init__(self, logger=None):
123
        if logger is None:
124
            logger = getLogger()
125
        self.logger = logger
5915.2.1 by Abel Deuring
replace the lxml module by elementtree and by xmllint for RelaxNG valdidation
126
        self.doc_parser = etree.XMLParser()
6533.1.2 by Abel Deuring
implemented reviewer's comments
127
        self._logged_warnings = set()
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
128
129
        self.validator = {}
5265.2.2 by Abel Deuring
implemented revier's comments
130
        directory = os.path.join(config.root, 'lib', 'canonical',
131
                                 'launchpad', 'scripts')
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
132
        for version, relax_ng_filename in _relax_ng_files.items():
5265.2.2 by Abel Deuring
implemented revier's comments
133
            path = os.path.join(directory, relax_ng_filename)
5915.2.1 by Abel Deuring
replace the lxml module by elementtree and by xmllint for RelaxNG valdidation
134
            self.validator[version] = RelaxNGValidator(path)
5628.8.3 by Abel Deuring
implemented reviewer's comments
135
        self._setMainSectionParsers()
136
        self._setHardwareSectionParsers()
137
        self._setSoftwareSectionParsers()
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
138
12751.2.4 by j.c.sackett
Added a context manager to allow the disabling of oops handling when needed in cronscripts, and updated tests.
139
    def _logError(self, message, submission_key, create_oops=True):
6261.2.3 by Abel Deuring
implemented reviewer's comments
140
        """Log `message` for an error in submission submission_key`."""
12751.2.4 by j.c.sackett
Added a context manager to allow the disabling of oops handling when needed in cronscripts, and updated tests.
141
        msg = 'Parsing submission %s: %s' % (submission_key, message)
142
        if not create_oops:
143
            with disable_oops_handler(self.logger):
144
                self.logger.error(msg)
145
        else:
146
            self.logger.error(msg)
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
147
6533.1.3 by Abel Deuring
implemented more reviewer comments.
148
    def _logWarning(self, message, warning_id=None):
6261.2.3 by Abel Deuring
implemented reviewer's comments
149
        """Log `message` for a warning in submission submission_key`."""
6533.1.3 by Abel Deuring
implemented more reviewer comments.
150
        if warning_id is None:
151
            issue_warning = True
152
        elif warning_id not in self._logged_warnings:
153
            issue_warning = True
154
            self._logged_warnings.add(warning_id)
155
        else:
156
            issue_warning = False
157
        if issue_warning:
158
            self.logger.warning(
159
                'Parsing submission %s: %s' % (self.submission_key, message))
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
160
5265.2.2 by Abel Deuring
implemented revier's comments
161
    def _getValidatedEtree(self, submission, submission_key):
162
        """Create an etree doc from the XML string submission and validate it.
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
163
5265.2.2 by Abel Deuring
implemented revier's comments
164
        :return: an `lxml.etree` instance representation of a valid
165
            submission or None for invalid submissions.
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
166
        """
167
        try:
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
168
            tree = etree.parse(StringIO(submission), parser=self.doc_parser)
5915.2.1 by Abel Deuring
replace the lxml module by elementtree and by xmllint for RelaxNG valdidation
169
        except SyntaxError, error_value:
5265.2.2 by Abel Deuring
implemented revier's comments
170
            self._logError(error_value, submission_key)
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
171
            return None
172
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
173
        submission_doc = tree.getroot()
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
174
        if submission_doc.tag != 'system':
5265.2.2 by Abel Deuring
implemented revier's comments
175
            self._logError("root node is not '<system>'", submission_key)
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
176
            return None
177
        version = submission_doc.attrib.get('version', None)
178
        if not version in self.validator.keys():
5265.2.2 by Abel Deuring
implemented revier's comments
179
            self._logError(
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
180
                'invalid submission format version: %s' % repr(version),
181
                submission_key)
182
            return None
183
        self.submission_format_version = version
184
185
        validator = self.validator[version]
5915.2.1 by Abel Deuring
replace the lxml module by elementtree and by xmllint for RelaxNG valdidation
186
        if not validator.validate(submission):
5265.2.2 by Abel Deuring
implemented revier's comments
187
            self._logError(
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
188
                'Relax NG validation failed.\n%s' % validator.error_log,
12751.2.4 by j.c.sackett
Added a context manager to allow the disabling of oops handling when needed in cronscripts, and updated tests.
189
                submission_key,
190
                create_oops=False)
5265.2.1 by Abel Deuring
Validaton of HWDB submissins, step 1
191
            return None
192
        return submission_doc
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
193
194
    def _getValueAttributeAsBoolean(self, node):
195
        """Return the value of the attribute "value" as a boolean."""
196
        value = node.attrib['value']
197
        # Paranoia check: The Relax NG validation already ensures that the
198
        # attribute value is either 'True' or 'False'.
199
        assert value in ('True', 'False'), (
200
            'Parsing submission %s: Boolean value for attribute "value" '
201
            'expected in tag <%s>' % (self.submission_key, node.tag))
202
        return value == 'True'
203
204
    def _getValueAttributeAsString(self, node):
5628.8.3 by Abel Deuring
implemented reviewer's comments
205
        """Return the value of the attribute "value"."""
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
206
        # The Relax NG validation ensures that the attribute exists.
207
        return node.attrib['value']
208
209
    def _getValueAttributeAsDateTime(self, time_node):
5628.8.2 by Abel Deuring
implemented reviewer's comments
210
        """Convert a "value" attribute into a datetime object."""
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
211
        time_text = time_node.get('value')
212
213
        # we cannot use time.strptime: this function accepts neither fractions
214
        # of a second nor a time zone given e.g. as '+02:30'.
215
        mo = _time_regex.search(time_text)
216
5628.8.2 by Abel Deuring
implemented reviewer's comments
217
        # The Relax NG schema allows a leading minus sign and year numbers
218
        # with more than four digits, which are not "covered" by _time_regex.
219
        if mo is None:
220
            raise ValueError(
221
                'Timestamp with unreasonable value: %s' % time_text)
222
223
        time_parts = mo.groupdict()
224
225
        year = int(time_parts['year'])
226
        month = int(time_parts['month'])
227
        day = int(time_parts['day'])
228
        hour = int(time_parts['hour'])
229
        minute = int(time_parts['minute'])
230
        second = int(time_parts['second'])
231
        second_fraction = time_parts['second_fraction']
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
232
        if second_fraction is not None:
5628.8.2 by Abel Deuring
implemented reviewer's comments
233
            milliseconds = second_fraction + '0' * (6 - len(second_fraction))
234
            milliseconds = int(milliseconds)
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
235
        else:
5628.8.2 by Abel Deuring
implemented reviewer's comments
236
            milliseconds = 0
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
237
238
        # The Relax NG validator accepts leap seconds, but the datetime
239
        # constructor rejects them. The time values submitted by the HWDB
240
        # client are not necessarily very precise, hence we can round down
241
        # to 59.999999 seconds without losing any real precision.
242
        if second > 59:
243
            second = 59
5628.8.2 by Abel Deuring
implemented reviewer's comments
244
            milliseconds = 999999
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
245
246
        timestamp = datetime(year, month, day, hour, minute, second,
5628.8.2 by Abel Deuring
implemented reviewer's comments
247
                             milliseconds, tzinfo=pytz.timezone('utc'))
248
249
        tz_sign = time_parts['tz_sign']
250
        tz_hour = time_parts['tz_hour']
251
        tz_minute = time_parts['tz_minute']
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
252
        if tz_sign in ('-', '+'):
253
            delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute))
254
            if tz_sign == '-':
255
                timestamp = timestamp + delta
256
            else:
257
                timestamp = timestamp - delta
258
        return timestamp
259
260
    def _getClientData(self, client_node):
261
        """Parse the <client> node in the <summary> section.
262
263
        :return: A dictionary with keys 'name', 'version', 'plugins'.
8037.2.1 by Brad Crittenden
fixed annoying typos
264
                 Name and version describe the client program that
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
265
                 produced the submission. Pugins is a list with one
266
                 entry per client plugin; each entry is dictionary with
267
                 the keys 'name' and 'version'.
268
        """
269
        result = {'name': client_node.get('name'),
270
                  'version': client_node.get('version')}
271
        plugins = result['plugins'] = []
272
        for node in client_node.getchildren():
273
            # Ensured by the Relax NG validation: The only allowed sub-tag
274
            # of <client> is <plugin>, which has the attributes 'name' and
275
            # 'version'.
276
            plugins.append({'name': node.get('name'),
277
                            'version': node.get('version')})
278
        return result
279
280
    _parse_summary_section = {
281
        'live_cd': _getValueAttributeAsBoolean,
282
        'system_id': _getValueAttributeAsString,
283
        'distribution': _getValueAttributeAsString,
284
        'distroseries': _getValueAttributeAsString,
285
        'architecture': _getValueAttributeAsString,
286
        'private': _getValueAttributeAsBoolean,
287
        'contactable': _getValueAttributeAsBoolean,
288
        'date_created': _getValueAttributeAsDateTime,
289
        'client': _getClientData,
9580.6.4 by Abel Deuring
Added ykernel-release> to the dictionary of known sub-nodes of <summary> for HWDB submissions
290
        'kernel-release': _getValueAttributeAsString,
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
291
        }
292
293
    def _parseSummary(self, summary_node):
294
        """Parse the <summary> part of a submission.
295
296
        :return: A dictionary with the keys 'live_cd', 'system_id',
297
                 'distribution', 'distroseries', 'architecture',
298
                 'private', 'contactable', 'date_created', 'client'.
299
                 See the sample XML file tests/hardwaretest.xml for
300
                 detailed description of the values.
301
        """
302
        summary = {}
303
        # The Relax NG validation ensures that we have exactly those
304
        # sub-nodes that are listed in _parse_summary_section.
305
        for node in summary_node.getchildren():
306
            parser = self._parse_summary_section[node.tag]
307
            summary[node.tag] = parser(self, node)
308
        return summary
309
310
    def _getValueAndType(self, node):
311
        """Return (value, type) of a <property> or <value> node."""
312
        type_ = node.get('type')
313
        if type_ in ('dbus.Boolean', 'bool'):
314
            value = node.text.strip()
315
            # Pure paranoia: The Relax NG validation ensures that <property>
316
            # and <value> tags have only the allowed values.
317
            assert value in ('True', 'False'), (
318
                'Parsing submission %s: Invalid bool value for <property> or '
319
                    '<value>: %s' % (self.submission_key, repr(value)))
320
            return (value == 'True', type_)
321
        elif type_ in ('str', 'dbus.String', 'dbus.UTF8String'):
5628.8.3 by Abel Deuring
implemented reviewer's comments
322
            return (node.text.strip(), type_)
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
323
        elif type_ in ('dbus.Byte', 'dbus.Int16', 'dbus.Int32', 'dbus.Int64',
324
                       'dbus.UInt16', 'dbus.UInt32', 'dbus.UInt64', 'int',
325
                       'long'):
326
            value = node.text.strip()
327
            return (int(value), type_)
328
        elif type_ in ('dbus.Double', 'float'):
329
            value = node.text.strip()
330
            return (float(value), type_)
331
        elif type_ in ('dbus.Array', 'list'):
332
            value = []
333
            for sub_node in node.getchildren():
334
                value.append(self._getValueAndType(sub_node))
335
            return (value, type_)
336
        elif type_ in ('dbus.Dictionary', 'dict'):
337
            value = {}
338
            for sub_node in node.getchildren():
339
                value[sub_node.get('name')] = self._getValueAndType(sub_node)
340
            return (value, type_)
341
        else:
342
            # This should not happen: The Relax NG validation ensures
343
            # that we have only those values for type_ that appear in
344
            # the if/elif expressions above.
345
            raise AssertionError(
346
                'Parsing submission %s: Unexpected <property> or <value> '
5628.8.3 by Abel Deuring
implemented reviewer's comments
347
                    'type: %s' % (self.submission_key, type_))
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
348
349
    def _parseProperty(self, property_node):
350
        """Parse a <property> node.
351
352
        :return: (name, (value, type)) of a property.
353
        """
354
        property_name = property_node.get('name')
355
        return (property_node.get('name'),
356
                self._getValueAndType(property_node))
357
358
    def _parseProperties(self, properties_node):
359
        """Parse <property> sub-nodes of properties_node.
360
361
        :return: A dictionary, where each key is the name of a property;
362
                 the values are the tuples (value, type) of a property.
363
        """
364
        properties = {}
365
        for property_node in properties_node.getchildren():
366
            # Paranoia check: The Relax NG schema ensures that a node
367
            # with <property> sub-nodes has no other sub-nodes
368
            assert property_node.tag == 'property', (
369
            'Parsing submission %s: Found <%s> node, expected <property>'
370
                % (self.submission_key, property_node.tag))
371
            property_name, property_value = self._parseProperty(property_node)
372
            if property_name in properties.keys():
373
                raise ValueError(
374
                    '<property name="%s"> found more than once in <%s>'
375
                    % (property_name, properties_node.tag))
376
            properties[property_name] = property_value
377
        return properties
378
379
    def _parseDevice(self, device_node):
380
        """Parse a HAL <device> node.
381
382
        :return: A dictionary d with the keys 'id', 'udi', 'parent',
383
                 'properties'. d['id'] is an ID of the device d['udi']
384
                 is the HAL UDI of the device; d['properties'] is a
385
                 dictionary with the properties of the device (see
386
                 _parseProperties for details).
387
        """
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
388
        # The Relax NG validation ensures that the attributes "id" and
389
        # "udi" exist; it also ensures that "id" contains an integer.
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
390
        device_data = {'id': int(device_node.get('id')),
5628.8.3 by Abel Deuring
implemented reviewer's comments
391
                       'udi': device_node.get('udi')}
392
        parent = device_node.get('parent', None)
393
        if parent is not None:
394
            parent = int(parent.strip())
395
        device_data['parent'] = parent
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
396
        device_data['properties'] = self._parseProperties(device_node)
397
        return device_data
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
398
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
399
    def _parseHAL(self, hal_node):
400
        """Parse the <hal> section of a submission.
401
402
        :return: A list, where each entry is the result of a _parseDevice
403
                 call.
404
        """
405
        # The Relax NG validation ensures that <hal> has the attribute
406
        # "version"
407
        hal_data = {'version': hal_node.get('version')}
408
        hal_data['devices'] = devices = []
409
        for device_node in hal_node.getchildren():
410
            # Pure paranoia: The Relax NG validation ensures already
411
            # that we have only <device> tags within <hal>
412
            assert device_node.tag == 'device', (
413
                'Parsing submission %s: Unexpected tag <%s> in <hal>'
414
                % (self.submission_key, device_node.tag))
415
            devices.append(self._parseDevice(device_node))
416
        return hal_data
417
418
    def _parseProcessors(self, processors_node):
419
        """Parse the <processors> node.
420
421
        :return: A list of dictionaries, where each dictionary d contains
422
                 the data of a <processor> node. The dictionary keys are
423
                 'id', 'name', 'properties'. d['id'] is an ID of a
424
                 <processor> node, d['name'] its name, and d['properties']
425
                 contains the properties of a processor (see
426
                 _parseProperties for details).
427
        """
428
        result = []
429
        for processor_node in processors_node.getchildren():
430
            # Pure paranoia: The Relax NG valiation ensures already
431
            # the we have only <processor> as sub-tags of <processors>.
432
            assert processor_node.tag == 'processor', (
433
                'Parsing submission %s: Unexpected tag <%s> in <processors>'
434
                   % (self.submission_key, processors_node.tag))
435
            processor = {}
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
436
            # The RelaxNG validation ensures that the attribute "id" exists
437
            # and that it contains an integer.
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
438
            processor['id'] = int(processor_node.get('id'))
439
            processor['name'] = processor_node.get('name')
440
            processor['properties'] = self._parseProperties(processor_node)
441
            result.append(processor)
442
        return result
443
444
    def _parseAliases(self, aliases_node):
445
        """Parse the <aliases> node.
446
447
        :return: A list of dictionaries, where each dictionary d has the
448
                 keys 'id', 'vendor', 'model'. d['id'] is the ID of a
449
                 HAL device; d['vendor'] is an alternative vendor name of
450
                 the device; d['model'] is an alternative model name.
451
452
                 See tests/hardwaretest.xml more more details.
453
        """
454
        aliases = []
455
        for alias_node in aliases_node.getchildren():
456
            # Pure paranoia: The Relax NG valiation ensures already
457
            # the we have only <alias> tags within <aliases>
458
            assert alias_node.tag == 'alias', (
459
                'Parsing submission %s: Unexpected tag <%s> in <aliases>'
460
                    % (self.submission_key, alias_node.tag))
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
461
            # The RelaxNG validation ensures that the attribute "target"
462
            # exists and that it contains an integer.
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
463
            alias = {'target': int(alias_node.get('target'))}
464
            for sub_node in alias_node.getchildren():
465
                # The Relax NG svalidation ensures that we have exactly
466
                # two subnodes: <vendor> and <model>
467
                alias[sub_node.tag] = sub_node.text.strip()
468
            aliases.append(alias)
469
        return aliases
470
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
471
    def _parseUdev(self, udev_node):
472
        """Parse the <udev> node.
473
9443.2.2 by Abel Deuring
implemented reviewer's comments
474
        :return: A list of dictionaries, where each dictionary
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
475
            describes a udev device.
476
477
        The <udev> node contains the output produced by
478
        "udevadm info --export-db". Each entry of the dictionaries
479
        represents the data of the key:value pairs as they appear
480
        in this data. The value of d['S'] is a list of strings,
481
        the value s['E'] is a dictionary containing the key=value
482
        pairs of the "E:" lines.
483
        """
484
        # We get the plain text as produced by "udevadm info --export-db"
485
        # This data looks like:
486
        #
487
        # P: /devices/LNXSYSTM:00
488
        # E: UDEV_LOG=3
489
        # E: DEVPATH=/devices/LNXSYSTM:00
490
        # E: MODALIAS=acpi:LNXSYSTM:
491
        #
492
        # P: /devices/LNXSYSTM:00/ACPI_CPU:00
493
        # E: UDEV_LOG=3
494
        # E: DEVPATH=/devices/LNXSYSTM:00/ACPI_CPU:00
495
        # E: DRIVER=processor
496
        # E: MODALIAS=acpi:ACPI_CPU:
497
        #
498
        # Data for different devices is separated by empty lines.
499
        # Each line for a device consists of key:value pairs.
500
        # The following keys are defined:
501
        #
502
        # A: udev_device_get_num_fake_partitions()
503
        # E: udev_device_get_properties_list_entry()
504
        # L: the device link priority (udev_device_get_devlink_priority())
505
        # N: the device node file name (udev_device_get_devnode())
506
        # P: the device path (udev_device_get_devpath())
507
        # R: udev_device_get_ignore_remove()
508
        # S: udev_get_dev_path()
509
        # W: udev_device_get_watch_handle()
510
        #
511
        # The key P is always present; the keys A, L, N, R, W appear at
512
        # most once per device; the keys E and S may appear more than
513
        # once.
514
        # The values of the E records have the format "key=value"
515
        #
516
        # See also the libudev reference manual:
517
        # http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/
518
        # and the udev file udevadm-info.c, function print_record()
519
520
        udev_data = udev_node.text.split('\n')
521
        devices = []
522
        device = None
523
        line_number = 0
9747.1.1 by Abel Deuring
Property UdevDevice.id added
524
        device_id = 0
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
525
9443.2.2 by Abel Deuring
implemented reviewer's comments
526
        for line_number, line in enumerate(udev_data):
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
527
            if len(line) == 0:
528
                device = None
529
                continue
530
            record = line.split(':', 1)
531
            if len(record) != 2:
532
                self._logError(
533
                    'Line %i in <udev>: No valid key:value data: %r'
534
                    % (line_number, line),
535
                    self.submission_key)
536
                return None
537
538
            key, value = record
539
            if device is None:
9747.1.1 by Abel Deuring
Property UdevDevice.id added
540
                device_id += 1
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
541
                device = {
542
                    'E': {},
543
                    'S': [],
9747.1.1 by Abel Deuring
Property UdevDevice.id added
544
                    'id': device_id,
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
545
                    }
546
                devices.append(device)
547
            # Some attribute lines have a space character after the
548
            # ':', others don't have it (see udevadm-info.c).
549
            value = value.lstrip()
550
551
            if key == 'E':
552
                property_data = value.split('=', 1)
553
                if len(property_data) != 2:
554
                    self._logError(
555
                        'Line %i in <udev>: Property without valid key=value '
556
                        'data: %r' % (line_number, line),
557
                        self.submission_key)
558
                    return None
559
                property_key, property_value = property_data
560
                device['E'][property_key] = property_value
561
            elif key == 'S':
562
                device['S'].append(value)
563
            else:
564
                if key in device:
565
                    self._logWarning(
566
                        'Line %i in <udev>: Duplicate attribute key: %r'
567
                        % (line_number, line),
568
                        self.submission_key)
569
                device[key] = value
570
        return devices
7844.3.2 by Abel Deuring
fine adjustment to the changes to exactly match the previous version
571
9580.2.1 by Abel Deuring
method HWSubmissionParser._parseDmi() added
572
    def _parseDmi(self, dmi_node):
573
        """Parse the <dmi> node.
574
575
        :return: A dictionary containing the key:value pairs of the DMI data.
576
        """
577
        dmi_data = {}
578
        dmi_text = dmi_node.text.strip().split('\n')
579
        for line_number, line in enumerate(dmi_text):
580
            record = line.split(':', 1)
581
            if len(record) != 2:
582
                self._logError(
583
                    'Line %i in <dmi>: No valid key:value data: %r'
584
                    % (line_number, line),
585
                    self.submission_key)
586
                return None
587
            dmi_data[record[0]] = record[1]
588
        return dmi_data
589
9580.6.1 by Abel Deuring
method SubmissionParser._parseSysfsAttributes() added
590
    def _parseSysfsAttributes(self, sysfs_node):
591
        """Parse the <sysfs-attributes> node.
592
593
        :return: A dictionary {path: attrs, ...} where path is the
594
            path is the path of a sysfs directory, and where attrs
595
            is a dictionary containing attribute names and values.
596
597
        A sample of the input data:
598
599
        P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
600
        A: modalias=input:b0019v0000p0001e0000-e0,1,k74,ramlsfw
601
        A: uniq=
602
        A: phys=LNXPWRBN/button/input0
603
        A: name=Power Button
604
605
        P: /devices/LNXSYSTM:00/device:00/PNP0A08:00/device:03/input/input8
606
        A: modalias=input:b0019v0000p0006e0000-e0,1,kE0,E1,E3,F0,F1
607
        A: uniq=
608
        A: phys=/video/input0
609
        A: name=Video Bus
610
611
        Data for different devices is separated by empty lines. The data
612
        for each device starts with a line 'P: /devices/LNXSYSTM...',
613
        specifying the sysfs path of a device, followed by zero or more
614
        lines of the form 'A: key=value'
615
        """
616
        sysfs_lines = sysfs_node.text.split('\n')
617
        sysfs_data = {}
618
        attributes = None
619
620
        for line_number, line in enumerate(sysfs_lines):
621
            if len(line) == 0:
622
                attributes = None
623
                continue
624
            record = line.split(': ', 1)
625
            if len(record) != 2:
626
                self._logError(
627
                    'Line %i in <sysfs-attributes>: No valid key:value data: '
628
                    '%r' % (line_number, line),
629
                    self.submission_key)
630
                return None
631
632
            key, value = record
633
            if key == 'P':
634
                if attributes is not None:
635
                    self._logError(
636
                        "Line %i in <sysfs-attributes>: duplicate 'P' line "
637
                        "found: %r" % (line_number, line),
638
                        self.submission_key)
639
                    return None
640
                attributes = {}
641
                sysfs_data[value] = attributes
642
            elif key == 'A':
643
                if attributes is None:
644
                    self._logError(
645
                        "Line %i in <sysfs-attributes>: Block for a device "
646
                        "does not start with 'P:': %r" % (line_number, line),
647
                        self.submission_key)
648
                    return None
649
                attribute_data = value.split('=', 1)
650
                if len(attribute_data) != 2:
651
                    self._logError(
652
                        'Line %i in <sysfs-attributes>: Attribute line does '
653
                        'not contain key=value data: %r'
654
                        % (line_number, line),
655
                        self.submission_key)
656
                    return None
657
                attributes[attribute_data[0]] = attribute_data[1]
658
            else:
659
                self._logError(
660
                    'Line %i in <sysfs-attributes>: Unexpected key: %r'
661
                    % (line_number, line),
662
                    self.submission_key)
663
                return None
664
        return sysfs_data
665
5628.8.3 by Abel Deuring
implemented reviewer's comments
666
    def _setHardwareSectionParsers(self):
667
        self._parse_hardware_section = {
668
            'hal': self._parseHAL,
669
            'processors': self._parseProcessors,
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
670
            'aliases': self._parseAliases,
671
            'udev': self._parseUdev,
9580.2.1 by Abel Deuring
method HWSubmissionParser._parseDmi() added
672
            'dmi': self._parseDmi,
9580.6.1 by Abel Deuring
method SubmissionParser._parseSysfsAttributes() added
673
            'sysfs-attributes': self._parseSysfsAttributes,
9443.2.1 by Abel Deuring
parse content of the <udev> mode in HWDB submissions
674
            }
5628.8.3 by Abel Deuring
implemented reviewer's comments
675
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
676
    def _parseHardware(self, hardware_node):
677
        """Parse the <hardware> part of a submission.
678
679
        :return: A dictionary with the keys 'hal', 'processors', 'aliases',
680
                 where the values are the parsing results of _parseHAL,
681
                 _parseProcessors, _parseAliases.
682
        """
683
        hardware_data = {}
684
        for node in hardware_node.getchildren():
685
            parser = self._parse_hardware_section[node.tag]
5628.8.3 by Abel Deuring
implemented reviewer's comments
686
            result = parser(node)
9747.1.13 by Abel Deuring
propagate errros ocurring in SubmissionParser.parseHardware() up the call chain
687
            if result is None:
688
                return None
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
689
            hardware_data[node.tag] = result
690
        return hardware_data
691
692
    def _parseLSBRelease(self, lsb_node):
693
        """Parse the <lsb_release> part of a submission.
694
695
        :return: A dictionary with the content of the <properta> nodes
696
                 within the <lsb> node. See tests/hardwaretest.xml for
697
                 details.
698
        """
699
        return self._parseProperties(lsb_node)
700
701
    def _parsePackages(self, packages_node):
702
        """Parse the <packages> part of a submission.
703
704
        :return: A dictionary with one entry per <package> sub-node.
705
                 The key is the package name, the value a dictionary
706
                 containing the content of the <property> nodes within
707
                 <package>. See tests/hardwaretest.xml for more details.
708
        """
709
        packages = {}
710
        for package_node in packages_node.getchildren():
711
            # Pure paranoia: The Relax NG validation ensures already
712
            # that we have only <package> tags within <packages>.
713
            assert package_node.tag == 'package', (
714
                'Parsing submission %s: Unexpected tag <%s> in <packages>'
715
                % (self.submission_key, package_node.tag))
716
            package_name = package_node.get('name')
717
            if package_name in packages.keys():
718
                raise ValueError(
719
                    '<package name="%s"> appears more than once in <packages>'
720
                    % package_name)
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
721
            # The RelaxNG validation ensures that the attribute "id" exists
722
            # and that it contains an integer.
723
            package_data = {'id': int(package_node.get('id'))}
724
            package_data['properties'] = self._parseProperties(package_node)
725
            packages[package_name] = package_data
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
726
        return packages
727
728
    def _parseXOrg(self, xorg_node):
729
        """Parse the <xorg> part of a submission.
730
731
        :return: A dictionary with the keys 'version' and 'drivers'.
732
                 d['version'] is the xorg version; d['drivers'] is
733
                 a dictionary with one entry for each <driver> sub-node,
734
                 where the key is the driver name, the value is a dictionary
735
                 containing the attributes of the <driver> node. See
736
                 tests/hardwaretest.xml for more details.
737
        """
738
        xorg_data = {'version': xorg_node.get('version')}
739
        xorg_data['drivers'] = xorg_drivers = {}
740
        for driver_node in xorg_node.getchildren():
741
            # Pure paranoia: The Relax NG validation ensures already
742
            # that we have only <driver> tags within <xorg>.
743
            assert driver_node.tag == 'driver', (
744
                'Parsing submission %s: Unexpected tag <%s> in <xorg>'
745
                    % (self.submission_key, driver_node.tag))
746
            driver_info = dict(driver_node.attrib)
5840.2.2 by Abel Deuring
implemented reviewer's comments
747
            if 'device' in driver_info:
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
748
                # The Relax NG validation ensures that driver_info['device']
749
                # consists of only digits, if present.
750
                driver_info['device'] = int(driver_info['device'])
751
            driver_name = driver_info['name']
752
            if driver_name in xorg_drivers.keys():
753
                raise ValueError(
754
                    '<driver name="%s"> appears more than once in <xorg>'
755
                    % driver_name)
756
            xorg_drivers[driver_name] = driver_info
757
        return xorg_data
758
759
    _parse_software_section = {
760
        'lsbrelease': _parseLSBRelease,
761
        'packages': _parsePackages,
762
        'xorg': _parseXOrg}
763
5628.8.3 by Abel Deuring
implemented reviewer's comments
764
    def _setSoftwareSectionParsers(self):
765
        self._parse_software_section = {
766
            'lsbrelease': self._parseLSBRelease,
767
            'packages': self._parsePackages,
768
            'xorg': self._parseXOrg}
769
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
770
    def _parseSoftware(self, software_node):
771
        """Parse the <software> section of a submission.
772
773
        :return: A dictionary with the keys 'lsbrelease', 'packages',
774
                 'xorg', containing the parsing results of the respective
775
                 sub-nodes. The key 'lsbrelease' exists always; 'xorg'
776
                 and 'packages' are optional. See _parseLSBRelease,
777
                 _parsePackages, _parseXOrg for more details.
778
        """
779
        software_data = {}
780
        for node in software_node.getchildren():
781
            parser = self._parse_software_section[node.tag]
5628.8.3 by Abel Deuring
implemented reviewer's comments
782
            result = parser(node)
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
783
            software_data[node.tag] = result
7184.4.1 by Abel Deuring
ensure that the HWDB submission parser creates dummy data for the nodes <xorg> and <packages> if they do not appear in a submission. This fxies KeyErrors for submissions which do not contain these nodes
784
        # The nodes <packages> and <xorg> are optional. Ensure that
785
        # we have dummy entries in software_data for these nodes, if
7184.4.2 by Abel Deuring
implemented reviewer's comments
786
        # the nodes do not appear in a submission in order to avoid
7184.4.1 by Abel Deuring
ensure that the HWDB submission parser creates dummy data for the nodes <xorg> and <packages> if they do not appear in a submission. This fxies KeyErrors for submissions which do not contain these nodes
787
        # KeyErrors elsewhere in this module.
788
        for node_name in ('packages', 'xorg'):
789
            if node_name not in software_data:
790
                software_data[node_name] = {}
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
791
        return software_data
792
793
    def _parseQuestions(self, questions_node):
794
        """Parse the <questions> part of a submission.
795
796
        :return: A list, where each entry is a dictionary containing
797
                 the parsing result of the <question> sub-nodes.
798
799
                 Content of a list entry d (see tests/hardwaretest.xml
800
                 for a more detailed description):
801
                 d['name']:
802
                        The name of a question. (Always present)
803
                 d['plugin']:
804
                        The name of the client plugin which is
805
                        "responsible" for the question. (Optional)
806
                 d['targets']:
807
                        A list, where each entry is a dicitionary
808
                        describing a target device for this question.
809
                        This list is always present, but may be empty.
810
811
                        The contents of each list entry t is:
812
813
                        t['id']:
814
                                The ID of a HAL <device> node of a
815
                                target device.
816
                        t['drivers']:
817
                                A list of driver names, possibly empty.
818
                 d['answer']:
819
                        The answer to this question. The value is a
820
                        dictionary a:
821
                        a['value']:
822
                                The value of the answer. (Always present)
823
824
                                For questions of type muliple_choice,
825
                                the value should match one of the
826
                                entries of the answer_choices list,
827
828
                                For questions of type measurement, the
829
                                value is a numerical value.
830
                        a['type']:
831
                                This is either 'multiple_choice' or
832
                                'measurement'. (Always present)
833
                        a['unit']:
834
                                The unit of a measurement value.
835
                                (Optional)
836
                 d['answer_choices']:
837
                        A list of choices from which the user can select
838
                        an answer. This list is always present, but should
839
                        be empty for questions of type measurement.
840
                 d['command']:
841
                        The command line of a test script which was
842
                        run for this question. (Optional)
843
                 d['comment']:
844
                        A comment the user has typed when running the
845
                        client. (Optional)
846
847
                 A consistency check of the content of d is done in
848
                 method _checkSubmissionConsistency.
849
        """
850
        questions = []
851
        for question_node in questions_node.getchildren():
852
            # Pure paranoia: The Relax NG validation ensures already
853
            # that we have only <driver> tags within <xorg>
854
            assert question_node.tag == 'question', (
855
                'Parsing submission %s: Unexpected tag <%s> in <questions>'
856
                % (self.submission_key, question_node.tag))
857
            question = {'name': question_node.get('name')}
858
            plugin = question_node.get('plugin', None)
859
            if plugin is not None:
860
                question['plugin'] = plugin
861
            question['targets'] = targets = []
862
            answer_choices = []
863
864
            for sub_node in question_node.getchildren():
865
                sub_tag = sub_node.tag
866
                if sub_tag == 'answer':
867
                    question['answer'] = answer = {}
868
                    answer['type'] = sub_node.get('type')
869
                    if answer['type'] == 'multiple_choice':
870
                        question['answer_choices'] = answer_choices
871
                    unit = sub_node.get('unit', None)
872
                    if unit is not None:
873
                        answer['unit'] = unit
874
                    answer['value'] = sub_node.text.strip()
875
                elif sub_tag == 'answer_choices':
876
                    for value_node in sub_node.getchildren():
877
                        answer_choices.append(
878
                            self._getValueAndType(value_node))
879
                elif sub_tag == 'target':
880
                    # The Relax NG schema ensures that the attribute
881
                    # id exists and that it is an integer
882
                    target = {'id': int(sub_node.get('id'))}
883
                    target['drivers'] = drivers = []
884
                    for driver_node in sub_node.getchildren():
885
                        drivers.append(driver_node.text.strip())
886
                    targets.append(target)
887
                elif sub_tag in('comment', 'command'):
888
                    data = sub_node.text
889
                    if data is not None:
890
                        question[sub_tag] = data.strip()
891
                else:
892
                    # This should not happen: The Relax NG validation
893
                    # ensures that we have only those tags which appear
894
                    # in the if/elif expressions.
895
                    raise AssertionError(
896
                        'Parsing submission %s: Unexpected node <%s> in '
897
                        '<question>' % (self.submission_key, sub_tag))
898
            questions.append(question)
899
        return questions
900
7844.3.3 by Abel Deuring
Nodes <context> and <info> added to the Relax NG schema for HWDB submissions.
901
    def _parseContext(self, context_node):
902
        """Parse the <context> part of a submission.
903
904
        We don't do anything real right now, but simply log a warning
905
        that this submission contains a <context> section, so that
906
        we can parse it again later, once we have the SQL tables needed
907
        to store the data.
908
        """
909
        self._logWarning('Submission contains unprocessed <context> data.')
9747.1.13 by Abel Deuring
propagate errros ocurring in SubmissionParser.parseHardware() up the call chain
910
        return {}
7844.3.3 by Abel Deuring
Nodes <context> and <info> added to the Relax NG schema for HWDB submissions.
911
5628.8.3 by Abel Deuring
implemented reviewer's comments
912
    def _setMainSectionParsers(self):
913
        self._parse_system = {
914
            'summary': self._parseSummary,
915
            'hardware': self._parseHardware,
916
            'software': self._parseSoftware,
7844.3.3 by Abel Deuring
Nodes <context> and <info> added to the Relax NG schema for HWDB submissions.
917
            'questions': self._parseQuestions,
918
            'context': self._parseContext,
919
            }
5628.8.3 by Abel Deuring
implemented reviewer's comments
920
921
    def parseMainSections(self, submission_doc):
922
        # The RelaxNG validation ensures that submission_doc has exactly
923
        # four sub-nodes and that the names of the sub-nodes appear in the
924
        # keys of self._parse_system.
925
        submission_data = {}
926
        try:
927
            for node in submission_doc.getchildren():
928
                parser = self._parse_system[node.tag]
9747.1.13 by Abel Deuring
propagate errros ocurring in SubmissionParser.parseHardware() up the call chain
929
                result = parser(node)
930
                if result is None:
931
                    return None
932
                submission_data[node.tag] = result
5628.8.3 by Abel Deuring
implemented reviewer's comments
933
        except ValueError, value:
934
            self._logError(value, self.submission_key)
935
            return None
936
        return submission_data
5628.8.1 by Abel Deuring
create an internal representation of HWDB submissions
937
938
    def parseSubmission(self, submission, submission_key):
939
        """Parse the data of a HWDB submission.
940
941
        :return: A dictionary with the keys 'summary', 'hardware',
942
                 'software', 'questions'. See _parseSummary,
943
                 _parseHardware, _parseSoftware, _parseQuestions for
944
                 the content.
945
        """
946
        self.submission_key = submission_key
947
        submission_doc  = self._getValidatedEtree(submission, submission_key)
948
        if submission_doc is None:
949
            return None
950
5628.8.3 by Abel Deuring
implemented reviewer's comments
951
        return self.parseMainSections(submission_doc)
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
952
953
    def _findDuplicates(self, all_ids, test_ids):
954
        """Search for duplicate elements in test_ids.
955
956
        :return: A set of those elements in the sequence test_ids that
957
        are elements of the set all_ids or that appear more than once
958
        in test_ids.
959
960
        all_ids is updated with test_ids.
961
        """
962
        duplicates = set()
5840.2.2 by Abel Deuring
implemented reviewer's comments
963
        # Note that test_ids itself may contain an ID more than once.
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
964
        for test_id in test_ids:
965
            if test_id in all_ids:
966
                duplicates.add(test_id)
967
            else:
968
                all_ids.add(test_id)
969
        return duplicates
970
971
    def findDuplicateIDs(self, parsed_data):
972
        """Return the set of duplicate IDs.
973
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
974
        The IDs of devices, processors and software packages should be
975
        unique; this method returns a list of duplicate IDs found in a
976
        submission.
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
977
        """
978
        all_ids = set()
9637.2.1 by Abel Deuring
consistency checks for HWDDB submissions with udev data
979
        if 'hal' in parsed_data['hardware']:
980
            duplicates = self._findDuplicates(
981
                all_ids,
982
                [device['id']
983
                 for device in parsed_data['hardware']['hal']['devices']])
984
        else:
985
            duplicates = self._findDuplicates(
986
                all_ids,
987
                [device['P']
988
                 for device in parsed_data['hardware']['udev']])
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
989
        duplicates.update(self._findDuplicates(
990
            all_ids,
991
            [processor['id']
992
             for processor in parsed_data['hardware']['processors']]))
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
993
        duplicates.update(self._findDuplicates(
994
            all_ids,
995
            [package['id']
996
             for package in parsed_data['software']['packages'].values()]))
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
997
        return duplicates
998
999
    def _getIDMap(self, parsed_data):
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
1000
        """Return a dictionary ID -> devices, processors and packages."""
1001
        id_map = {}
9637.2.1 by Abel Deuring
consistency checks for HWDDB submissions with udev data
1002
        if 'hal' in parsed_data['hardware']:
1003
            hal_devices = parsed_data['hardware']['hal']['devices']
1004
            for device in hal_devices:
1005
                id_map[device['id']] = device
1006
        else:
1007
            udev_devices = parsed_data['hardware']['udev']
1008
            for device in udev_devices:
1009
                id_map[device['P']] = device
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1010
1011
        for processor in parsed_data['hardware']['processors']:
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
1012
            id_map[processor['id']] = processor
1013
1014
        for package in parsed_data['software']['packages'].values():
1015
            id_map[package['id']] = package
1016
1017
        return id_map
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1018
1019
    def findInvalidIDReferences(self, parsed_data):
1020
        """Return the set of invalid references to IDs.
1021
5915.3.1 by Abel Deuring
HWDB Relax NG schema modification: stricter rules for IDs; missing attribute 'id' for <package> added
1022
        The sub-tag <target> of <question> references a device, processor
1023
        of package node by its ID; the submission must contain a <device>,
1024
        <processor> or <software> tag with this ID. This method returns a
1025
        set of those IDs mentioned in <target> nodes that have no
1026
        corresponding device or processor node.
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1027
        """
1028
        id_device_map = self._getIDMap(parsed_data)
1029
        known_ids = set(id_device_map.keys())
1030
        questions = parsed_data['questions']
1031
        target_lists = [question['targets'] for question in questions]
1032
        all_targets = []
1033
        for target_list in target_lists:
1034
            all_targets.extend(target_list)
5840.2.2 by Abel Deuring
implemented reviewer's comments
1035
        all_target_ids = set(target['id'] for target in all_targets)
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1036
        return all_target_ids.difference(known_ids)
1037
1038
    def getUDIDeviceMap(self, devices):
1039
        """Return a dictionary which maps UDIs to HAL devices.
1040
8294.5.1 by Abel Deuring
Fix for bug 336613: ignore certain duplicate UDIs in HWDB submissions
1041
        Also check, if a UDI is used more than once.
1042
1043
        Generally, a duplicate UDI indicates a bad or bogus submission,
1044
        but we have some UDIs where the duplicate UDI is caused by a
1045
        bug in HAL, see
1046
        http://lists.freedesktop.org/archives/hal/2009-April/013250.html
1047
        In these cases, we simply remove the duplicates, otherwise, a
1048
        ValueError is raised.
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1049
        """
1050
        udi_device_map = {}
8294.5.1 by Abel Deuring
Fix for bug 336613: ignore certain duplicate UDIs in HWDB submissions
1051
        duplicates = []
1052
        for index in xrange(len(devices)):
1053
            device = devices[index]
1054
            udi = device['udi']
1055
            if udi in udi_device_map:
1056
                if 'info.parent' in device['properties']:
1057
                    parent_udi = device['properties']['info.parent'][0]
1058
                else:
1059
                    parent_udi = None
1060
                if (udi in KNOWN_DUPLICATE_UDIS or
1061
                    parent_udi in KNOWN_DUPLICATE_UDIS):
1062
                    duplicates.append(index)
1063
                    continue
1064
                else:
1065
                    raise ValueError('Duplicate UDI: %s' % device['udi'])
5840.2.2 by Abel Deuring
implemented reviewer's comments
1066
            else:
8294.5.1 by Abel Deuring
Fix for bug 336613: ignore certain duplicate UDIs in HWDB submissions
1067
                udi_device_map[udi] = device
1068
        duplicates.reverse()
1069
        for index in duplicates:
1070
            devices.pop(index)
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1071
        return udi_device_map
1072
1073
    def _getIDUDIMaps(self, devices):
1074
        """Return two mappings describing the relation between IDs and UDIs.
1075
1076
        :return: two dictionaries id_to_udi and udi_to_id, where
1077
                 id_2_udi has IDs as keys and UDI as values, and where
1078
                 udi_to_id has UDIs as keys and IDs as values.
1079
        """
1080
        id_to_udi = {}
1081
        udi_to_id = {}
1082
        for device in devices:
1083
            id = device['id']
1084
            udi = device['udi']
1085
            id_to_udi[id] = udi
1086
            udi_to_id[udi] = id
1087
        return id_to_udi, udi_to_id
1088
1089
    def getUDIChildren(self, udi_device_map):
1090
        """Build lists of all children of a UDI.
1091
1092
        :return: A dictionary that maps UDIs to lists of children.
1093
5840.2.2 by Abel Deuring
implemented reviewer's comments
1094
        If any info.parent property points to a non-existing existing
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1095
        device, a ValueError is raised.
1096
        """
1097
        # Each HAL device references its parent device (HAL attribute
1098
        # info.parent), except for the "root node", which has no parent.
1099
        children = {}
1100
        known_udis = set(udi_device_map.keys())
1101
        for device in udi_device_map.values():
5840.2.2 by Abel Deuring
implemented reviewer's comments
1102
            parent_property = device['properties'].get('info.parent', None)
1103
            if parent_property is not None:
1104
                parent = parent_property[0]
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1105
                if not parent in known_udis:
1106
                    raise ValueError(
1107
                        'Unknown parent UDI %s in <device id="%s">'
1108
                        % (parent, device['id']))
5840.2.2 by Abel Deuring
implemented reviewer's comments
1109
                if parent in children:
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1110
                    children[parent].append(device)
1111
                else:
1112
                    children[parent] = [device]
1113
            else:
1114
                # A node without a parent is a root node. Only one root node
1115
                # is allowed, which must have the UDI
1116
                # "/org/freedesktop/Hal/devices/computer".
1117
                # Other nodes without a parent UDI indicate an error, as well
1118
                # as a non-existing root node.
5840.2.2 by Abel Deuring
implemented reviewer's comments
1119
                if device['udi'] != ROOT_UDI:
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1120
                    raise ValueError(
1121
                        'root device node found with unexpected UDI: '
1122
                        '<device id="%s" udi="%s">' % (device['id'],
1123
                                                       device['udi']))
1124
5840.2.2 by Abel Deuring
implemented reviewer's comments
1125
        if not ROOT_UDI in children:
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1126
            raise ValueError('No root device found')
1127
        return children
1128
1129
    def _removeChildren(self, udi, udi_test):
1130
        """Remove recursively all children of the device named udi."""
5840.2.2 by Abel Deuring
implemented reviewer's comments
1131
        if udi in udi_test:
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1132
            children = udi_test[udi]
1133
            for child in children:
1134
                self._removeChildren(child['udi'], udi_test)
1135
            del udi_test[udi]
1136
1137
    def checkHALDevicesParentChildConsistency(self, udi_children):
1138
        """Ensure that HAL devices are represented in exactly one tree.
1139
1140
        :return: A list of those UDIs that are not "connected" to the root
1141
                 node /org/freedesktop/Hal/devices/computer
1142
1143
        HAL devices "know" their parent device; each device has a parent,
1144
        except the root element. This means that it is possible to traverse
1145
        all existing devices, beginning at the root node.
1146
1147
        Several inconsistencies are possible:
1148
1149
        (a) we may have more than one root device (i.e., one without a
1150
            parent)
1151
        (b) we may have no root element
1152
        (c) circular parent/child references may exist.
1153
1154
        (a) and (b) are already checked in _getUDIChildren; this method
1155
        implements (c),
1156
        """
1157
        # If we build a copy of udi_children and if we remove, starting at
1158
        # the root UDI, recursively all children from this copy, we should
1159
        # get a dictionary, where all values are empty lists. Any remaining
1160
        # nodes must have circular parent references.
1161
1162
        udi_test = {}
1163
        for udi, children in udi_children.items():
1164
            udi_test[udi] = children[:]
5840.2.2 by Abel Deuring
implemented reviewer's comments
1165
        self._removeChildren(ROOT_UDI, udi_test)
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1166
        return udi_test.keys()
1167
9637.2.1 by Abel Deuring
consistency checks for HWDDB submissions with udev data
1168
    def checkUdevDictsHavePathKey(self, udev_nodes):
1169
        """Ensure that each udev dictionary has a 'P' key.
1170
1171
        The 'P' (path) key identifies a device.
1172
        """
1173
        for node in udev_nodes:
1174
            if not 'P' in node:
1175
                self._logError('udev node found without a "P" key',
1176
                               self.submission_key)
1177
                return False
1178
        return True
1179
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1180
    PCI_PROPERTIES = set(
1181
        ('PCI_CLASS', 'PCI_ID', 'PCI_SUBSYS_ID', 'PCI_SLOT_NAME'))
1182
    pci_class_re = re.compile('^[0-9a-f]{1,6}$', re.I)
1183
    pci_id_re = re.compile('^[0-9a-f]{4}:[0-9a-f]{4}$', re.I)
1184
1185
    def checkUdevPciProperties(self, udev_data):
1186
        """Validation of udev PCI devices.
1187
9644.3.6 by Abel Deuring
implemented reviewer's comments
1188
        :param udev_data: A list of dicitionaries describing udev
1189
             devices, as returned by _parseUdev()
1190
        :return: True if all checks pass, else False.
1191
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1192
        Each PCI device must have the properties PCI_CLASS, PCI_ID,
1193
        PCI_SUBSYS_ID, PCI_SLOT_NAME. Non-PCI devices must not have
1194
        them.
1195
1196
        The value of PCI class must be a 24 bit integer in
1197
        hexadecimal representation.
1198
1199
        The values of PCI_ID and PCI_SUBSYS_ID must be two 16 bit
1200
        integers, separated by a ':'.
1201
        """
1202
        for device in udev_data:
1203
            properties = device['E']
9644.3.8 by Abel Deuring
implemented reviewer's comments
1204
            property_names = set(properties)
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1205
            existing_pci_properties = property_names.intersection(
1206
                self.PCI_PROPERTIES)
1207
            subsystem = device['E'].get('SUBSYSTEM')
1208
            if subsystem is None:
1209
                self._logError(
1210
                    'udev device without SUBSYSTEM property found.',
1211
                    self.submission_key)
1212
                return False
1213
            if subsystem == 'pci':
9644.3.6 by Abel Deuring
implemented reviewer's comments
1214
                # Check whether any of the standard pci properties were
1215
                # missing.
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1216
                if existing_pci_properties != self.PCI_PROPERTIES:
9644.3.3 by Abel Deuring
improved tests for udev sanity checks
1217
                    missing_properties = self.PCI_PROPERTIES.difference(
1218
                            existing_pci_properties)
1219
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1220
                    self._logError(
9644.3.3 by Abel Deuring
improved tests for udev sanity checks
1221
                        'PCI udev device without required PCI properties: '
1222
                            '%r %r'
1223
                            % (missing_properties, device['P']),
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1224
                        self.submission_key)
1225
                    return False
9644.3.6 by Abel Deuring
implemented reviewer's comments
1226
                # Ensure that the pci class and ids for this device are
1227
                # formally valid.
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1228
                if self.pci_class_re.search(properties['PCI_CLASS']) is None:
1229
                    self._logError(
9644.3.3 by Abel Deuring
improved tests for udev sanity checks
1230
                        'Invalid udev PCI class: %r %r'
1231
                            % (properties['PCI_CLASS'], device['P']),
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1232
                        self.submission_key)
1233
                    return False
1234
                for pci_id in (properties['PCI_ID'],
1235
                               properties['PCI_SUBSYS_ID']):
1236
                    if self.pci_id_re.search(pci_id) is None:
1237
                        self._logError(
9644.3.3 by Abel Deuring
improved tests for udev sanity checks
1238
                            'Invalid udev PCI device ID: %r %r'
1239
                                % (pci_id, device['P']),
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1240
                            self.submission_key)
1241
                        return False
1242
            else:
1243
                if len(existing_pci_properties) > 0:
1244
                    self._logError(
9644.3.3 by Abel Deuring
improved tests for udev sanity checks
1245
                        'Non-PCI udev device with PCI properties: %r %r'
1246
                            % (existing_pci_properties, device['P']),
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1247
                        self.submission_key)
1248
                    return False
9644.3.3 by Abel Deuring
improved tests for udev sanity checks
1249
        return True
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1250
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1251
    def checkUdevUsbProperties(self, udev_data):
1252
        """Validation of udev USB devices.
1253
9747.1.9 by Abel Deuring
validate nodes for USB devices in HWDB submissions with udev data that do not have the properties DEVTYPE, PRODUCT, TYPE
1254
        USB devices must either have the three properties DEVTYPE
1255
        (value 'usb_device' or 'usb_interface'), PRODUCT and TYPE,
1256
        or they must have none of them.
1257
1258
        PRODUCT must be a tuple of three integers in hexadecimal
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1259
        representation, separates by '/'. TYPE must be a a tuple of
1260
        three integers in decimal representation, separated by '/'.
1261
        usb_interface nodes must additionally have a property
1262
        INTERFACE, containing three integers in the same format as
1263
        TYPE.
1264
        """
1265
        for device in udev_data:
1266
            subsystem = device['E'].get('SUBSYSTEM')
1267
            if subsystem != 'usb':
1268
                continue
1269
            properties = device['E']
9644.3.8 by Abel Deuring
implemented reviewer's comments
1270
            property_names = set(properties)
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1271
            existing_usb_properties = property_names.intersection(
9644.3.8 by Abel Deuring
implemented reviewer's comments
1272
                UDEV_USB_DEVICE_PROPERTIES)
9747.1.9 by Abel Deuring
validate nodes for USB devices in HWDB submissions with udev data that do not have the properties DEVTYPE, PRODUCT, TYPE
1273
1274
            if len(existing_usb_properties) == 0:
1275
                continue
1276
9644.3.8 by Abel Deuring
implemented reviewer's comments
1277
            if existing_usb_properties != UDEV_USB_DEVICE_PROPERTIES:
1278
                missing_properties = UDEV_USB_DEVICE_PROPERTIES.difference(
1279
                    existing_usb_properties)
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1280
                self._logError(
1281
                    'USB udev device found without required properties: %r %r'
9644.3.8 by Abel Deuring
implemented reviewer's comments
1282
                    % (missing_properties, device['P']),
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1283
                    self.submission_key)
1284
                return False
9644.3.8 by Abel Deuring
implemented reviewer's comments
1285
            if UDEV_USB_PRODUCT_RE.search(properties['PRODUCT']) is None:
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1286
                self._logError(
1287
                    'USB udev device found with invalid product ID: %r %r'
1288
                    % (properties['PRODUCT'], device['P']),
1289
                    self.submission_key)
1290
                return False
9644.3.8 by Abel Deuring
implemented reviewer's comments
1291
            if UDEV_USB_TYPE_RE.search(properties['TYPE']) is None:
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1292
                self._logError(
1293
                    'USB udev device found with invalid type data: %r %r'
1294
                    % (properties['TYPE'], device['P']),
1295
                    self.submission_key)
1296
                return False
1297
1298
            device_type = properties['DEVTYPE']
1299
            if device_type not in ('usb_device', 'usb_interface'):
1300
                self._logError(
1301
                    'USB udev device found with invalid udev type data: %r %r'
1302
                    % (device_type, device['P']),
1303
                    self.submission_key)
1304
                return False
1305
            if device_type == 'usb_interface':
1306
                interface_type = properties.get('INTERFACE')
1307
                if interface_type is None:
1308
                    self._logError(
1309
                        'USB interface udev device found without INTERFACE '
1310
                        'property: %r'
1311
                        % device['P'],
1312
                        self.submission_key)
1313
                    return False
9644.3.8 by Abel Deuring
implemented reviewer's comments
1314
                if UDEV_USB_TYPE_RE.search(interface_type) is None:
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1315
                    self._logError(
1316
                        'USB Interface udev device found with invalid '
1317
                        'INTERFACE property: %r %r'
1318
                        % (interface_type, device['P']),
1319
                        self.submission_key)
1320
                    return False
1321
        return True
1322
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
1323
    def checkUdevScsiProperties(self, udev_data, sysfs_data):
1324
        """Validation of udev SCSI devices.
1325
1326
        Each udev node where SUBSYSTEM is 'scsi' should have the
1327
        property DEVTYPE; nodes where DEVTYPE is 'scsi_device'
1328
        should have a corresponding sysfs node, and this node should
1329
        define the attributes 'vendor', 'model', 'type'.
1330
        """
1331
        for device in udev_data:
1332
            subsystem = device['E'].get('SUBSYSTEM')
1333
            if subsystem != 'scsi':
1334
                continue
1335
            properties = device['E']
1336
            if 'DEVTYPE' not in properties:
1337
                self._logError(
1338
                    'SCSI udev node found without DEVTYPE property: %r'
1339
                    % device['P'],
1340
                    self.submission_key)
1341
                return False
1342
            if properties['DEVTYPE'] == 'scsi_device':
1343
                device_path = device['P']
1344
                if device_path not in sysfs_data:
1345
                    self._logError(
1346
                        'SCSI udev device node found without related '
1347
                        'sysfs record: %r' % device_path,
1348
                        self.submission_key)
1349
                    return False
1350
                sysfs_attributes = sysfs_data[device_path]
1351
                sysfs_attribute_names = set(sysfs_attributes)
1352
                if SYSFS_SCSI_DEVICE_ATTRIBUTES.intersection(
1353
                    sysfs_attribute_names) != SYSFS_SCSI_DEVICE_ATTRIBUTES:
1354
                    missing_attributes = (
1355
                        SYSFS_SCSI_DEVICE_ATTRIBUTES.difference(
1356
                            sysfs_attribute_names))
1357
                    self._logError(
1358
                        'SCSI udev device found without required sysfs '
1359
                        'attributes: %r %r'
1360
                        % (missing_attributes, device_path),
1361
                        self.submission_key)
1362
                    return False
1363
        return True
1364
1365
    def checkUdevDmiData(self, dmi_data):
1366
        """Consistency check for DMI data.
1367
1368
        All keys of the dictionary dmi_data should start with
1369
        '/sys/class/dmi/id/'.
1370
        """
1371
        for dmi_key in dmi_data:
1372
            if not dmi_key.startswith('/sys/class/dmi/id/'):
1373
                self._logError(
1374
                    'Invalid DMI key: %r' % dmi_key, self.submission_key)
1375
                return False
1376
        return True
1377
1378
    def checkConsistentUdevDeviceData(self, udev_data, sysfs_data, dmi_data):
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1379
        """Consistency checks for udev data."""
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
1380
        return (
1381
            self.checkUdevDictsHavePathKey(udev_data) and
1382
            self.checkUdevPciProperties(udev_data) and
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
1383
            self.checkUdevUsbProperties(udev_data) and
1384
            self.checkUdevScsiProperties(udev_data, sysfs_data) and
1385
            self.checkUdevDmiData(dmi_data))
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1386
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1387
    def checkConsistency(self, parsed_data):
5840.2.2 by Abel Deuring
implemented reviewer's comments
1388
        """Run consistency checks on the submitted data.
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1389
5840.2.2 by Abel Deuring
implemented reviewer's comments
1390
        :return: True, if the data looks consistent, otherwise False.
1391
        :param: parsed_data: parsed submission data, as returned by
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1392
                             parseSubmission
1393
        """
9644.3.2 by Abel Deuring
RF merge; conflicts resolved
1394
        if ('udev' in parsed_data['hardware']
1395
            and not self.checkConsistentUdevDeviceData(
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
1396
                parsed_data['hardware']['udev'],
9747.1.5 by Abel Deuring
Fixed a bad method call in the HWDB submission processing script; defined a new function validating method signatures in mock classes for testing; replaced monkey-patching of class SubmissionParser by mock classes in tests; validated the the mock classes with the new function.
1397
                parsed_data['hardware']['sysfs-attributes'],
1398
                parsed_data['hardware']['dmi'],)):
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
1399
            return False
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1400
        duplicate_ids = self.findDuplicateIDs(parsed_data)
1401
        if duplicate_ids:
1402
            self._logError('Duplicate IDs found: %s' % duplicate_ids,
1403
                           self.submission_key)
1404
            return False
1405
1406
        invalid_id_references = self.findInvalidIDReferences(parsed_data)
1407
        if invalid_id_references:
1408
            self._logError(
1409
                'Invalid ID references found: %s' % invalid_id_references,
1410
                self.submission_key)
1411
            return False
1412
9637.2.1 by Abel Deuring
consistency checks for HWDDB submissions with udev data
1413
        if 'hal' in parsed_data['hardware']:
1414
            try:
1415
                udi_device_map = self.getUDIDeviceMap(
1416
                    parsed_data['hardware']['hal']['devices'])
1417
                udi_children = self.getUDIChildren(udi_device_map)
1418
            except ValueError, value:
1419
                self._logError(value, self.submission_key)
1420
                return False
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1421
9644.3.4 by Abel Deuring
fixed a lint complaint about a too long line
1422
            circular = self.checkHALDevicesParentChildConsistency(
1423
                udi_children)
9637.2.1 by Abel Deuring
consistency checks for HWDDB submissions with udev data
1424
            if circular:
1425
                self._logError('Found HAL devices with circular parent/child '
1426
                               'relationship: %s' % circular,
1427
                               self.submission_key)
1428
                return False
5840.2.1 by Abel Deuring
consistency checks for HWDB submissions
1429
1430
        return True
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1431
1432
    def buildDeviceList(self, parsed_data):
1433
        """Create a list of devices from a submission."""
9710.4.7 by Abel Deuring
renamed SubmissionParser.buildDeviceList() to buildHalDeviceList(); new method buildDeviceList(); SubmissionParser.hal_devices renamed to SubmissionParser.devices
1434
        if 'hal' in parsed_data['hardware']:
1435
            return self.buildHalDeviceList(parsed_data)
1436
        else:
1437
            return self.buildUdevDeviceList(parsed_data)
1438
1439
    def buildHalDeviceList(self, parsed_data):
1440
        """Create a list of devices from the HAL data of a submission."""
9710.4.8 by Abel Deuring
implemented reviewer's comments
1441
        self.devices = {}
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1442
        for hal_data in parsed_data['hardware']['hal']['devices']:
1443
            udi = hal_data['udi']
9710.4.8 by Abel Deuring
implemented reviewer's comments
1444
            self.devices[udi] = HALDevice(hal_data['id'], udi,
1445
                                          hal_data['properties'], self)
1446
        for device in self.devices.values():
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1447
            parent_udi = device.parent_udi
1448
            if parent_udi is not None:
9710.4.8 by Abel Deuring
implemented reviewer's comments
1449
                self.devices[parent_udi].addChild(device)
9710.4.7 by Abel Deuring
renamed SubmissionParser.buildDeviceList() to buildHalDeviceList(); new method buildDeviceList(); SubmissionParser.hal_devices renamed to SubmissionParser.devices
1450
        return True
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1451
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1452
    def buildUdevDeviceList(self, parsed_data):
1453
        """Create a list of devices from the udev data of a submission."""
9710.4.8 by Abel Deuring
implemented reviewer's comments
1454
        self.devices = {}
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1455
        sysfs_data = parsed_data['hardware']['sysfs-attributes']
1456
        dmi_data = parsed_data['hardware']['dmi']
1457
        for udev_data in parsed_data['hardware']['udev']:
1458
            device_path = udev_data['P']
1459
            if device_path == UDEV_ROOT_PATH:
1460
                device = UdevDevice(
1461
                    self, udev_data, sysfs_data=sysfs_data.get(device_path),
1462
                    dmi_data=dmi_data)
1463
            else:
1464
                device = UdevDevice(
1465
                    self, udev_data, sysfs_data=sysfs_data.get(device_path))
9710.4.8 by Abel Deuring
implemented reviewer's comments
1466
            self.devices[device_path] = device
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1467
1468
        # The parent-child relations are derived from the path names of
1469
        # the devices. If A and B are the path names of two devices,
1470
        # the device with path name A is an ancestor of the device with
1471
        # path name B, iff B.startswith(A). If C is the set of the path
1472
        # names of all ancestors of A, the element with the longest path
1473
        # name belongs to the parent of A.
1474
        #
1475
        # There is one exception to this rule: The root node has the
1476
        # the path name '/devices/LNXSYSTM:00', while the path names
1477
        # of PCI devices start with '/devices/pci'. We'll temporarily
1478
        # change the path name of the root device so that the rule
1479
        # holds for all devices.
9710.4.8 by Abel Deuring
implemented reviewer's comments
1480
        if UDEV_ROOT_PATH not in self.devices:
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1481
            self._logError('No udev root device defined', self.submission_key)
1482
            return False
9710.4.8 by Abel Deuring
implemented reviewer's comments
1483
        self.devices['/devices'] = self.devices[UDEV_ROOT_PATH]
1484
        del self.devices[UDEV_ROOT_PATH]
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1485
9710.4.8 by Abel Deuring
implemented reviewer's comments
1486
        path_names = sorted(self.devices, key=len, reverse=True)
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1487
        for path_index, path_name in enumerate(path_names[:-1]):
1488
            # Ensure that the last ancestor of each device is our
1489
            # root node.
1490
            if not path_name.startswith('/devices'):
1491
                self._logError(
1492
                    'Invalid device path name: %r' % path_name,
1493
                    self.submission_key)
1494
                return False
1495
            for parent_path in path_names[path_index+1:]:
1496
                if path_name.startswith(parent_path):
9710.4.8 by Abel Deuring
implemented reviewer's comments
1497
                    self.devices[parent_path].addChild(
1498
                        self.devices[path_name])
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1499
                    break
9710.4.8 by Abel Deuring
implemented reviewer's comments
1500
        self.devices[UDEV_ROOT_PATH] = self.devices['/devices']
1501
        del self.devices['/devices']
9710.4.4 by Abel Deuring
new method SubmissionParser.buildUdevDeviceList()
1502
        return True
1503
9747.1.4 by Abel Deuring
changed SubmissionParser.getKernelPackageName into property SubmissionParser.kernel_package_name; added code to kernel_package_name to deal with data from HWDB submissions with ude data.
1504
    @cachedproperty
1505
    def kernel_package_name(self):
1506
        """The kernel package name for the submission."""
1507
        if ROOT_UDI in self.devices:
1508
            root_hal_device = self.devices[ROOT_UDI]
1509
            kernel_version = root_hal_device.getProperty(
1510
                'system.kernel.version')
1511
        else:
1512
            kernel_version = self.parsed_data['summary'].get('kernel-release')
6533.1.2 by Abel Deuring
implemented reviewer's comments
1513
        if kernel_version is None:
6533.1.3 by Abel Deuring
implemented more reviewer comments.
1514
            self._logWarning(
1515
                'Submission does not provide property system.kernel.version '
9747.1.4 by Abel Deuring
changed SubmissionParser.getKernelPackageName into property SubmissionParser.kernel_package_name; added code to kernel_package_name to deal with data from HWDB submissions with ude data.
1516
                'for /org/freedesktop/Hal/devices/computer or a summary '
1517
                'sub-node <kernel-release>.')
6533.1.2 by Abel Deuring
implemented reviewer's comments
1518
            return None
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
1519
        kernel_package_name = 'linux-image-' + kernel_version
6533.1.2 by Abel Deuring
implemented reviewer's comments
1520
        packages = self.parsed_data['software']['packages']
7230.3.2 by Abel Deuring
implemented reviewer's comments
1521
        # The submission is not required to provide any package data...
1522
        if packages and kernel_package_name not in packages:
1523
            # ...but if we have it, we want it to be consistent with
1524
            # the HAL root node property.
1525
            self._logWarning(
1526
                'Inconsistent kernel version data: According to HAL the '
1527
                'kernel is %s, but the submission does not know about a '
1528
                'kernel package %s'
9747.1.4 by Abel Deuring
changed SubmissionParser.getKernelPackageName into property SubmissionParser.kernel_package_name; added code to kernel_package_name to deal with data from HWDB submissions with ude data.
1529
                % (kernel_version, kernel_package_name))
7230.3.2 by Abel Deuring
implemented reviewer's comments
1530
            return None
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
1531
        return kernel_package_name
1532
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
1533
    def processSubmission(self, submission):
1534
        """Process a submisson.
1535
1536
        :return: True, if the submission could be sucessfully processed,
1537
            otherwise False.
1538
        :param submission: An IHWSubmission instance.
1539
        """
1540
        raw_submission = submission.raw_submission
7881.6.5 by Abel Deuring
shortened the time the HWDB processing script waits for a response from the Librarian
1541
        # This script runs once per day and can need a few hours to run.
1542
        # Short-lived Librarian server failures or a server restart should
1543
        # not break this script, so let's wait for 10 minutes for a
1544
        # response from the Librarian.
1545
        raw_submission.open(timeout=600)
1546
        submission_data = raw_submission.read(timeout=600)
7881.6.4 by Abel Deuring
re-implementation of handling Librarian errors in scripts/hwdbsubmissions.py, based on better error handling in FileDownloadClient.getFileByAlias().
1547
        raw_submission.close()
1548
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
1549
        # We assume that the data has been sent bzip2-compressed,
1550
        # but this is not checked when the data is submitted.
1551
        expanded_data = None
1552
        try:
1553
            expanded_data = bz2.decompress(submission_data)
1554
        except IOError:
1555
            # An IOError is raised, if the data is not BZip2-compressed.
1556
            # We assume in this case that valid uncompressed data has been
1557
            # submitted. If this assumption is wrong, parseSubmission()
1558
            # or checkConsistency() will complain, hence we don't check
1559
            # anything else here.
1560
            pass
1561
        if expanded_data is not None:
1562
            submission_data = expanded_data
1563
1564
        parsed_data = self.parseSubmission(
1565
            submission_data, submission.submission_key)
1566
        if parsed_data is None:
1567
            return False
1568
        self.parsed_data = parsed_data
1569
        if not self.checkConsistency(parsed_data):
1570
            return False
9710.4.7 by Abel Deuring
renamed SubmissionParser.buildDeviceList() to buildHalDeviceList(); new method buildDeviceList(); SubmissionParser.hal_devices renamed to SubmissionParser.devices
1571
        if not self.buildDeviceList(parsed_data):
1572
            return False
9747.1.11 by Abel Deuring
new property SubmissionParser.root_device; replaced access to self.udi by self.device_id in BaseDevice.is_real_device
1573
        self.root_device.createDBData(submission, None)
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
1574
        return True
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1575
9747.1.11 by Abel Deuring
new property SubmissionParser.root_device; replaced access to self.udi by self.device_id in BaseDevice.is_real_device
1576
    @property
1577
    def root_device(self):
1578
        """The HALDevice of UdevDevice node of the root device."""
1579
        # checkConsistency ensures that we have either a device with the
9747.1.12 by Abel Deuring
fixed a typo
1580
        # key ROOT_UDI or a device with the key UDEV_ROOT_PATH.
9747.1.11 by Abel Deuring
new property SubmissionParser.root_device; replaced access to self.udi by self.device_id in BaseDevice.is_real_device
1581
        if ROOT_UDI in self.devices:
1582
            return self.devices[ROOT_UDI]
1583
        else:
1584
            return self.devices[UDEV_ROOT_PATH]
1585
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1586
1587
class BaseDevice:
1588
    """A base class to represent device data from HAL and udev."""
9600.1.2 by Abel Deuring
Implemented reviewer's comments
1589
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1590
    def __init__(self, parser):
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1591
        self.children = []
1592
        self.parser = parser
6261.2.3 by Abel Deuring
implemented reviewer's comments
1593
        self.parent = None
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1594
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1595
    # Translation of the HAL info.bus/info.subsystem property and the
1596
    # udev property SUBSYSTEM to HWBus enumerated buses.
1597
    subsystem_hwbus = {
1598
        'pcmcia': HWBus.PCMCIA,
1599
        'usb_device': HWBus.USB,
1600
        'ide': HWBus.IDE,
1601
        'serio': HWBus.SERIAL,
1602
        }
1603
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1604
    def addChild(self, child):
6261.2.2 by Abel Deuring
implemented reviewer's comments
1605
        """Add a child device and set the child's parent."""
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1606
        assert type(child) == type(self)
1607
        self.children.append(child)
6261.2.2 by Abel Deuring
implemented reviewer's comments
1608
        child.parent = self
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1609
1610
    # Translation of subclasses of the PCI class storage to HWBus
6261.2.2 by Abel Deuring
implemented reviewer's comments
1611
    # enumerated buses. The Linux kernel accesses IDE and SATA disks
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1612
    # and CDROM drives via the SCSI system; we want to know the real bus
1613
    # of the drive. See for example the file include/linux/pci_ids.h
1614
    # in the Linux kernel sources for a list of PCI device classes and
1615
    # subclasses. Note that the subclass 4 (RAID) is missing. While it
1616
    # may make sense to declare a RAID storage class for PCI devices,
1617
    # "RAID" does not tell us anything about the bus of the storage
1618
    # devices.
1619
    pci_storage_subclass_hwbus = {
1620
        0: HWBus.SCSI,
1621
        1: HWBus.IDE,
1622
        2: HWBus.FLOPPY,
1623
        3: HWBus.IPI, # Intelligent Peripheral Interface
1624
        5: HWBus.ATA,
1625
        6: HWBus.SATA,
1626
        7: HWBus.SAS,
1627
        }
1628
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1629
    @property
1630
    def device_id(self):
1631
        """A unique ID for this device."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1632
        raise NotImplementedError
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1633
1634
    @property
1635
    def pci_class(self):
1636
        """The PCI device class of the device or None for Non-PCI devices."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1637
        raise NotImplementedError
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1638
1639
    @property
1640
    def pci_subclass(self):
1641
        """The PCI device sub-class of the device or None for Non-PCI devices.
1642
        """
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1643
        raise NotImplementedError
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1644
9600.1.3 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 2
1645
    @property
1646
    def usb_vendor_id(self):
1647
        """The USB vendor ID of the device or None for Non-USB devices."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1648
        raise NotImplementedError
9600.1.3 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 2
1649
1650
    @property
1651
    def usb_product_id(self):
1652
        """The USB product ID of the device or None for Non-USB devices."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1653
        raise NotImplementedError
9600.1.3 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 2
1654
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1655
    @property
1656
    def scsi_vendor(self):
1657
        """The SCSI vendor name of the device or None for Non-SCSI devices."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1658
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1659
1660
    @property
1661
    def scsi_model(self):
1662
        """The SCSI model name of the device or None for Non-SCSI devices."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1663
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1664
1665
    @property
1666
    def vendor(self):
1667
        """The vendor of this device."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1668
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1669
1670
    @property
1671
    def product(self):
1672
        """The vendor of this device."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1673
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1674
1675
    @property
1676
    def vendor_id(self):
1677
        """The vendor ID of this device."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1678
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1679
1680
    @property
1681
    def product_id(self):
1682
        """The product ID of this device."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1683
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1684
1685
    @property
1686
    def vendor_id_for_db(self):
1687
        """The vendor ID in the representation needed for the HWDB tables.
1688
1689
        USB and PCI IDs are represented in the database in hexadecimal,
1690
        while the IDs provided by HAL are integers.
1691
1692
        The SCSI vendor name is right-padded with spaces to 8 bytes.
1693
        """
1694
        bus = self.raw_bus
1695
        format = DB_FORMAT_FOR_VENDOR_ID.get(bus)
1696
        if format is None:
1697
            return self.vendor_id
1698
        else:
1699
            return format % self.vendor_id
1700
1701
    @property
1702
    def product_id_for_db(self):
1703
        """The product ID in the representation needed for the HWDB tables.
1704
1705
        USB and PCI IDs are represented in the database in hexadecimal,
1706
        while the IDs provided by HAL are integers.
1707
1708
        The SCSI product name is right-padded with spaces to 16 bytes.
1709
        """
1710
        bus = self.raw_bus
1711
        format = DB_FORMAT_FOR_PRODUCT_ID.get(bus)
1712
        if format is None:
1713
            return self.product_id
1714
        else:
1715
            return format % self.product_id
1716
1717
    @property
1718
    def driver_name(self):
1719
        """The name of the driver contolling this device. May be None."""
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
1720
        raise NotImplementedError
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
1721
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
1722
    @property
1723
    def scsi_controller(self):
1724
        """Return the SCSI host controller for this device."""
1725
        raise NotImplementedError
1726
6261.2.3 by Abel Deuring
implemented reviewer's comments
1727
    def translateScsiBus(self):
7178.2.1 by Abel Deuring
improved detection of the device bus in class HALDevice
1728
        """Return the real bus of a device where raw_bus=='scsi'.
6261.2.3 by Abel Deuring
implemented reviewer's comments
1729
1730
        The kernel uses the SCSI layer to access storage devices
1731
        connected via the USB, IDE, SATA buses. See `is_real_device`
1732
        for more details. This method determines the real bus
1733
        of a device accessed via the kernel's SCSI subsystem.
1734
        """
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
1735
        scsi_controller = self.scsi_controller
1736
        if scsi_controller is None:
6261.2.3 by Abel Deuring
implemented reviewer's comments
1737
            return None
1738
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
1739
        scsi_controller_bus = scsi_controller.raw_bus
1740
        if scsi_controller_bus == 'pci':
1741
            if (scsi_controller.pci_class != PCI_CLASS_STORAGE):
6261.2.3 by Abel Deuring
implemented reviewer's comments
1742
                # This is not a storage class PCI device? This
1743
                # indicates a bug somewhere in HAL or in the hwdb
1744
                # client, or a fake submission.
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
1745
                device_class = scsi_controller.pci_class
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
1746
                self.parser._logWarning(
6261.2.3 by Abel Deuring
implemented reviewer's comments
1747
                    'A (possibly fake) SCSI device %s is connected to '
1748
                    'PCI device %s that has the PCI device class %s; '
6533.1.2 by Abel Deuring
implemented reviewer's comments
1749
                    'expected class 1 (storage).'
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
1750
                    % (self.device_id, scsi_controller.device_id,
1751
                       device_class))
6261.2.3 by Abel Deuring
implemented reviewer's comments
1752
                return None
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
1753
            pci_subclass = scsi_controller.pci_subclass
6261.2.3 by Abel Deuring
implemented reviewer's comments
1754
            return self.pci_storage_subclass_hwbus.get(pci_subclass)
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
1755
        elif scsi_controller_bus in ('usb', 'usb_interface'):
6261.2.3 by Abel Deuring
implemented reviewer's comments
1756
            # USB storage devices have the following HAL device hierarchy:
1757
            # - HAL node for the USB device. info.bus == 'usb_device',
1758
            #   device class == 0, device subclass == 0
1759
            # - HAL node for the USB storage interface. info.bus == 'usb',
1760
            #   interface class 8, interface subclass 6
1761
            #   (see http://www.usb.org/developers/devclass_docs
1762
            #   /usb_msc_overview_1.2.pdf)
7178.2.1 by Abel Deuring
improved detection of the device bus in class HALDevice
1763
            # - HAL node for the (fake) SCSI host. raw_bus is None
1764
            # - HAL node for the (fake) SCSI device. raw_bus == 'scsi'
6261.2.3 by Abel Deuring
implemented reviewer's comments
1765
            # - HAL node for the mass storage device
1766
            #
1767
            # Physically, the storage device can be:
1768
            # (1) a genuine USB device, like a memory stick
1769
            # (2) a IDE/SATA hard disk, connected to a USB -> SATA/IDE
1770
            #     bridge
1771
            # (3) a card reader
1772
            # There is no formal way to distinguish cases (1) and (2):
1773
            # The device and interface classes are in both cases
1774
            # identical; the only way to figure out, if we have a USB
1775
            # hard disk enclosure or a USB memory stick would be to
1776
            # look at the vendor or product names, or to look up some
1777
            # external data sources. Of course, we can also ask the
1778
            # submitter in the future.
1779
            #
1780
            # The cases (1) and (2) differ from (3) in the property
1781
            # the property storage.removable. For (1) and (2), this
1782
            # property is False, for (3) it is True. Since we do not
1783
            # store at present any device characteristics in the HWDB,
1784
            # so there is no point to distinguish between (1), (2) on
1785
            # one side and (3) on the other side. Distinguishing
1786
            # between (1) and (2) might be more interesting, because
1787
            # a hard disk is clearly a separate device, but as written,
1788
            # it is hard to distinguish between (1) and (2)
1789
            #
1790
            # To sum up: we cannot get any interesting and reliable
1791
            # information about the details of USB storage device,
1792
            # so we'll treat those devices as "black boxes".
1793
            return None
1794
        else:
1795
            return HWBus.SCSI
6261.2.5 by Abel Deuring
implemented reviewer's commets
1796
6261.2.3 by Abel Deuring
implemented reviewer's comments
1797
    def translatePciBus(self):
1798
        # Cardbus (aka PCCard, sometimes also incorrectly called
1799
        # PCMCIA) devices are treated as PCI devices by the kernel.
1800
        # We can detect PCCards by checking that the parent device
1801
        # is a PCI bridge (device class 6) for the Cardbus (device
1802
        # subclass 7).
1803
        # XXX Abel Deuring 2005-05-14 How can we detect ExpressCards?
1804
        # I do not have any such card at present...
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1805
        parent_class = self.parent.pci_class
1806
        parent_subclass = self.parent.pci_subclass
6261.2.6 by Abel Deuring
implemented reviewer's comments
1807
        if (parent_class == PCI_CLASS_BRIDGE
1808
            and parent_subclass == PCI_SUBCLASS_BRIDGE_CARDBUS):
6261.2.3 by Abel Deuring
implemented reviewer's comments
1809
            return HWBus.PCCARD
1810
        else:
1811
            return HWBus.PCI
1812
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1813
    @property
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
1814
    def is_root_device(self):
1815
        """Return True is this is the root node of all devicese, else False.
1816
        """
1817
        raise NotImplementedError
1818
1819
    @property
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1820
    def raw_bus(self):
1821
        """Return the device bus as specified by HAL or udev."""
9600.1.6 by Abel Deuring
removed () from NotImplementedError()
1822
        raise NotImplementedError
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1823
7178.2.2 by Abel Deuring
changed method HALDevice.getRealBus() into property HALDevice.real_bus; fixed a typo
1824
    @property
1825
    def real_bus(self):
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1826
        """Return the bus this device connects to on the host side.
1827
1828
        :return: A bus as enumerated in HWBus or None, if the bus
1829
            cannot be determined.
1830
        """
7178.2.1 by Abel Deuring
improved detection of the device bus in class HALDevice
1831
        device_bus = self.raw_bus
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
1832
        result = self.subsystem_hwbus.get(device_bus)
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1833
        if result is not None:
1834
            return result
1835
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
1836
        if device_bus in ('scsi', 'scsi_device'):
6261.2.3 by Abel Deuring
implemented reviewer's comments
1837
            return self.translateScsiBus()
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1838
        elif device_bus == 'pci':
6261.2.3 by Abel Deuring
implemented reviewer's comments
1839
            return self.translatePciBus()
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
1840
        elif self.is_root_device:
6533.1.2 by Abel Deuring
implemented reviewer's comments
1841
            # The computer itself. In Hardy, HAL provides no info.bus
1842
            # for the machine itself; older versions set info.bus to
1843
            # 'unknown', hence it is better to use the machine's
1844
            # UDI.
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1845
            return HWBus.SYSTEM
1846
        else:
1847
            self.parser._logWarning(
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
1848
                'Unknown bus %r for device %s' % (device_bus, self.device_id))
6261.2.1 by Abel Deuring
Implementation of class HALDevice, part 1
1849
            return None
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
1850
1851
    @property
1852
    def is_real_device(self):
1853
        """True, if the HAL device correspends to a real device.
1854
1855
        In many cases HAL has more than one device entry for the
1856
        same physical device. We are only interested in real, physical,
1857
        devices but not in the fine details, how HAL represents different
1858
        aspects of them.
1859
1860
        For example, the HAL device node hiearchy for a SATA disk and
6261.2.5 by Abel Deuring
implemented reviewer's commets
1861
        its host controller looks like this:
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
1862
1863
        HAL device node of the host controller
1864
            udi: .../pci_8086_27c5
1865
            HAL properties:
1866
                info.bus: pci
1867
                pci.device_class: 1 (storage)
1868
                pci.device_subclass: 6 (SATA)
1869
                info.linux.driver: ahci
1870
1871
        HAL device node of the "output aspect" of the host controller
1872
            udi: .../pci_8086_27c5_scsi_host
1873
            HAL properties:
1874
                info.bus: n/a
1875
                info.driver: n/a
1876
                info.parent: .../pci_8086_27c5
1877
1878
        HAL device node of a hard disk.
1879
            udi: .../pci_8086_27c5_scsi_host_scsi_device_lun0
1880
            HAL properties:
1881
                info.bus: scsi
1882
                info.driver: sd
1883
                info.parent: .../pci_8086_27c5_scsi_host
1884
1885
        HAL device node of the "storage aspect" of the hard disk
1886
            udi: .../storage_serial_1ATA_Hitachi_HTS541616J9SA00_SB...
1887
            HAL properties
1888
                info.driver: n/a
1889
                info.parent: .../pci_8086_27c5_scsi_host_scsi_device_lun0
1890
1891
        HAL device node of a disk partition:
1892
            udi: .../volume_uuid_0ee803cf_...
1893
            HAL properties
1894
                info.driver: n/a
1895
                info.parent: .../storage_serial_1ATA_Hitachi_HTS541616J...
1896
1897
        (optionally more nodes for more partitions)
1898
1899
        HAL device node of the "generic SCSI aspect" of the hard disk:
1900
            udi: .../pci_8086_27c5_scsi_host_scsi_device_lun0_scsi_generic
1901
                info.driver: n/a
1902
                info.parent: .../pci_8086_27c5_scsi_host_scsi_device_lun0
1903
1904
        This disk is _not_ a SCSI disk, but a SATA disk. In other words,
1905
        the SCSI details are in this case just an artifact of the Linux
1906
        kernel, which uses its SCSI subsystem as a "central hub" to access
1907
        IDE, SATA, USB, IEEE1394 storage devices. The only interesting
1908
        detail for us is that the sd driver is involved in accesses to the
1909
        disk.
1910
1911
        Heuristics:
1912
1913
        - Most real devices have the property info.bus; we consider only
1914
          those devices to be real which have this property set.
1915
1916
        - As written above, the SCSI bus often appears as an artifact;
1917
          for PCI host controllers, their properties pci.device_class
6261.2.5 by Abel Deuring
implemented reviewer's commets
1918
          and pci.device_subclass tell us if we have a real SCSI host
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
1919
          controller: pci.device_class == 1 means a storage controller,
1920
          pci.device_subclass == 0 means a SCSI controller. This works
1921
          too for PCCard controllers, which use the PCI device class
1922
          numbers too.
1923
1924
        - The value "usb_device" of the HAL property info.bus identifies
1925
          USB devices, with one exception: The USB host controller, which
1926
          itself has an info.bus property with the value "pci", has a
1927
          sub-device with info.bus='usb_device' for its "output aspect".
1928
          These sub-devices can be identified by the device class their
1929
          parent and by their USB vendor/product IDs, which are 0:0.
7598.1.2 by Abel Deuring
implemented reviewer's comments
1930
1931
        Several info.bus/info.subsystem values always relate to HAL nodes
1932
        which describe only "aspects" of physical devcies which are
1933
        represented by other HAL nodes:
1934
1935
          - bus is None for a number of "virtual components", like
1936
            /org/freedesktop/Hal/devices/computer_alsa_timer or
1937
            /org/freedesktop/Hal/devices/computer_oss_sequencer, so
1938
            we ignore them. (The real sound devices appear with
1939
            other UDIs in HAL.)
1940
1941
            XXX Abel Deuring 20080425: This ignores a few components
1942
            like laptop batteries or the CPU, where info.bus is None.
1943
            Since these components are not the most important ones
1944
            for the HWDB, we'll ignore them for now. Bug 237038.
1945
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
1946
          - 'disk' is used udev submissions for a node related to the
1947
            sd or sr driver of (real or fake) SCSI block devices.
1948
7658.2.1 by Abel Deuring
Extended the list of device buses that should be ignored during HWDB submission processing.
1949
          - info.bus == 'drm' is used by the HAL for the direct
1950
            rendering interface of a graphics card.
1951
1952
          - info.bus == 'dvb' is used by HAL for the "input aspect"
1953
            of DVB receivers
1954
1955
          - info.bus == 'memstick_host' is used by HAL for the "output aspect"
1956
            of memory sticks.
1957
7598.1.2 by Abel Deuring
implemented reviewer's comments
1958
          - info.bus == 'net' is used by the HAL version in
1959
            Intrepid for the "output aspects" of network devices.
1960
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
1961
          - 'partition' is used in udev submissions for a node
1962
            related to disk partition
1963
1964
          - 'scsi_disk' is used in udev submissions for a sub-node of
1965
            the real device node.
1966
7598.1.2 by Abel Deuring
implemented reviewer's comments
1967
            info.bus == 'scsi_generic' is used by the HAL version in
1968
            Intrepid for a HAL node representing the generic
1969
            interface of a SCSI device.
1970
1971
            info.bus == 'scsi_host' is used by the HAL version in
1972
            Intrepid for real and "fake" SCSI host controllers.
1973
            (On Hardy, these nodes have no info.bus property.)
1974
            HAL nodes with this bus value are sub-nodes for the
1975
            "SCSI aspect" of another HAL node which represents the
1976
            real device.
1977
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
1978
            'scsi_target' is used in udev data for SCSI target nodes,
1979
            the parent of a SCSI device (or LUN) node.
1980
1981
            'spi_transport' (SCSI Parallel Transport) is used in
1982
            udev data for a sub-node of real SCSI devices.
1983
7598.1.2 by Abel Deuring
implemented reviewer's comments
1984
            info.bus == 'sound' is used by the HAL version in
1985
            Intrepid for "aspects" of sound devices.
1986
1987
            info.bus == 'ssb' is used for "aspects" of Broadcom
1988
            Ethernet and WLAN devices, but like 'usb', they do not
1989
            represent separate devices.
1990
7658.2.1 by Abel Deuring
Extended the list of device buses that should be ignored during HWDB submission processing.
1991
            info.bus == 'tty' is used for the "output aspect"
1992
            of serial output devices (RS232, modems etc). It appears
1993
            for USB and PCI devices as well as for legacy devices
1994
            like the 8250/16450/16550 controllers.
1995
7598.1.2 by Abel Deuring
implemented reviewer's comments
1996
            info.bus == 'usb' is used for end points of USB devices;
1997
            the root node of a USB device has info.bus == 'usb_device'.
7658.2.1 by Abel Deuring
Extended the list of device buses that should be ignored during HWDB submission processing.
1998
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
1999
            'usb_interface' is used in udv submissions for interface
2000
            nodes of USB devices.
2001
9600.1.3 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 2
2002
            info.bus == 'video4linux' is used for the "input aspect"
7658.2.1 by Abel Deuring
Extended the list of device buses that should be ignored during HWDB submission processing.
2003
            of video devices.
9747.1.10 by Abel Deuring
Added a number of buses that should treated as being 'unreal' or 'unprocessible' to BaseDevice.is_real_device and BaseDevice.has_reliable_data.
2004
2005
            'ac97' is used in submissions with udev data for a sub-node
2006
            of sound devices.
2007
2008
            'hid' is used in submissions with udev data for a sub-node
2009
            of USB input devices.
2010
2011
            'drm_minor', 'pci_express', 'tifm_adapter', 'gameport',
2012
            'spi_host', 'tifm', 'wlan' are used in submissions with
2013
            udev data for sub-nodes of PCI devices.
2014
2015
            'pcmcia_socket' is used in submissions with udev data for
2016
            a sub-node of PC Card and PCMCIA bridges.
2017
2018
            'ieee80211'  is used in submissions with udev data for
2019
            sub-nodes IEEE 802.11 WLAN devices.
2020
2021
            'host', 'link' are used in submissions with udev data for
2022
            sub.nodes of bluetooth devices.
2023
2024
            'usb_host' and 'usbmon' are used in submissions with udev
2025
            data for sub-nodes of USB controllers.
2026
2027
            'usb_endpoint', 'usb-serial', 'lirc' are used in
2028
            submissions with udev data for sub-nodes of USB devices.
2029
2030
            'enclosure' is used in submissions with udev data for a
2031
            sub.node of SCSI devices.
2032
2033
            'graphics' is used  in submissions with udev data for a
2034
            sub-node of graphics cards.
2035
2036
            'hwmon' is is used  in submissions with udev data in
2037
            many sub-nodes.
9830.7.1 by Abel Deuring
hwdbsubmissions.py: Extended the sets of bus names known to belong to virtual devices or used for nodes that describe only aspects of real devices
2038
2039
            'sas_phy', 'sas_device', 'sas_end_device', 'sas_port',
2040
            'sas_host' is used in submissions with udev data for
2041
            details of SAS controllers.
2042
2043
            'mISDN' is used  in submissions with udev data for the
2044
            I/O aspects of ISDN adapters.
2045
2046
            'pvrusb2' is used  in submissions with udev data for the
2047
            input aspect of some DVB adapters.
2048
2049
            'memstick' is used  in submissions with udev data for the
2050
            I/O aspect of memory stick controllers.
2051
2052
            'bttv-sub' is used  in submissions with udev data for the
2053
            I/O aspects of some TV receivers.
2054
2055
            'scsi_tape' is used  in submissions with udev data for
2056
            details of SCSI tape drives.
2057
            """
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
2058
        # The root node is always a real device, but its raw_bus
2059
        # property can have different values: None or 'Unknown' in
2060
        # submissions with HAL data, 'acpi' for submissions with udev
2061
        # data.
2062
        if self.is_root_device:
2063
            return True
2064
7465.6.1 by Abel Deuring
fix for bug 308510: Many devices ignored during submission processing when the bus is given in property info.subsystem, not info.bus
2065
        bus = self.raw_bus
7658.2.2 by Abel Deuring
implemented reviewer's comments
2066
        # This set of buses is only used once; it's easier to have it
2067
        # here than to put it elsewhere and have to document its
2068
        # location and purpose.
9830.7.1 by Abel Deuring
hwdbsubmissions.py: Extended the sets of bus names known to belong to virtual devices or used for nodes that describe only aspects of real devices
2069
        if bus in (None, 'ac97', 'bttv-sub', 'disk', 'drm', 'drm_minor',
2070
                   'dvb', 'enclosure', 'gameport', 'graphics', 'hid', 'host',
2071
                   'hwmon', 'ieee80211', 'link', 'lirc', 'mISDN', 'memstick',
2072
                   'memstick_host', 'net', 'partition', 'pci_express',
2073
                   'pcmcia_socket', 'pvrusb2', 'sas_device', 'sas_end_device',
2074
                   'sas_host', 'sas_phy', 'sas_port', 'scsi_disk',
2075
                   'scsi_generic', 'scsi_host', 'scsi_tape', 'scsi_target',
9747.1.10 by Abel Deuring
Added a number of buses that should treated as being 'unreal' or 'unprocessible' to BaseDevice.is_real_device and BaseDevice.has_reliable_data.
2076
                   'sound', 'spi_host', 'spi_transport', 'ssb', 'tifm',
2077
                   'tifm_adapter', 'tty', 'usb', 'usb-serial', 'usb_endpoint',
2078
                   'usb_host', 'usb_interface', 'usbmon', 'video4linux',
2079
                   'wlan'):
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
2080
            return False
6261.2.5 by Abel Deuring
implemented reviewer's commets
2081
        elif bus == 'usb_device':
9600.1.3 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 2
2082
            vendor_id = self.usb_vendor_id
2083
            product_id = self.usb_product_id
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2084
            if vendor_id == 0 and product_id == 0:
2085
                # double-check: The parent device should be a PCI host
2086
                # controller, identifiable by its device class and subclass.
6912.5.21 by Curtis Hovey
Updated the XXX comment's metadata using the bug found in the comment text.
2087
                # XXX Abel Deuring 2008-04-28 Bug=237039: This ignores other
2088
                # possible bridges, like ISA->USB..
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2089
                parent = self.parent
7178.2.1 by Abel Deuring
improved detection of the device bus in class HALDevice
2090
                parent_bus = parent.raw_bus
9600.1.1 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 1
2091
                parent_class = parent.pci_class
2092
                parent_subclass = parent.pci_subclass
6261.2.5 by Abel Deuring
implemented reviewer's commets
2093
                if (parent_bus == 'pci'
2094
                    and parent_class == PCI_CLASS_SERIALBUS_CONTROLLER
6261.2.6 by Abel Deuring
implemented reviewer's comments
2095
                    and parent_subclass == PCI_SUBCLASS_SERIALBUS_USB):
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2096
                    return False
2097
                else:
2098
                    self.parser._logWarning(
2099
                        'USB device found with vendor ID==0, product ID==0, '
2100
                        'where the parent device does not look like a USB '
9747.1.11 by Abel Deuring
new property SubmissionParser.root_device; replaced access to self.udi by self.device_id in BaseDevice.is_real_device
2101
                        'host controller: %s' % self.device_id)
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2102
                    return False
2103
            return True
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
2104
        elif bus in ('scsi', 'scsi_device'):
7178.2.2 by Abel Deuring
changed method HALDevice.getRealBus() into property HALDevice.real_bus; fixed a typo
2105
            # Ensure consistency with HALDevice.real_bus
2106
            return self.real_bus is not None
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2107
        else:
2108
            return True
2109
2110
    def getRealChildren(self):
2111
        """Return the list of real child devices of this devices.
2112
2113
        The list of real child devices consists of the direct child
2114
        devices of this device where child.is_real_device == True, and
2115
        of the (recursively collected) list of real sub-devices of
6261.2.5 by Abel Deuring
implemented reviewer's commets
2116
        those child devices where child.is_real_device == False.
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2117
        """
2118
        result = []
2119
        for sub_device in self.children:
2120
            if sub_device.is_real_device:
2121
                # XXX Abel Deuring 2008-05-06: IEEE1394 devices are a bit
2122
                # nasty: The standard does not define any specification
2123
                # for product IDs or product names, hence HAL often
2124
                # uses the value 0 for the property ieee1394.product_id
2125
                # and a value like "Unknown (0x00d04b)" for
2126
                # ieee.product, where 0x00d04b is the vendor ID. I have
2127
                # currently no idea how to find or generate something
2128
                # that could be used as the product ID, so IEEE1394
2129
                # devices are at present simply dropped from the list of
2130
                # devices. Otherwise, we'd pollute the HWDB with
6261.2.5 by Abel Deuring
implemented reviewer's commets
2131
                # unreliable data. Bug 237044.
7178.2.1 by Abel Deuring
improved detection of the device bus in class HALDevice
2132
                if sub_device.raw_bus != 'ieee1394':
6261.2.4 by Abel Deuring
Implementation of class HALDevice, part 2
2133
                    result.append(sub_device)
2134
            else:
2135
                result.extend(sub_device.getRealChildren())
2136
        return result
6261.2.8 by Abel Deuring
part 3 Implementation of class HALDevice, part 3: retrieval of vendor/product name and ID
2137
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2138
    @property
6533.1.2 by Abel Deuring
implemented reviewer's comments
2139
    def has_reliable_data(self):
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2140
        """Can this device be stored in the HWDB?
2141
2142
        Devices are identifed by (bus, vendor_id, product_id).
2143
        At present we cannot generate reliable vendor and/or product
7598.1.1 by Abel Deuring
several HAL bus/subsystem names added to the the set of ignored names
2144
        IDs for devices with the following values of the HAL
2145
        property info.bus resp. info.subsystem.
2146
2147
        info.bus == 'backlight' is used by the HAL version in
2148
        Intrepid for the LC display. Useful vendor and product names
2149
        are not available.
2150
7658.2.1 by Abel Deuring
Extended the list of device buses that should be ignored during HWDB submission processing.
2151
        info.bus == 'bluetooth': HAL does not provide any vendor/product
2152
        ID data, so we can't store these devices in HWDevice.
2153
7598.1.1 by Abel Deuring
several HAL bus/subsystem names added to the the set of ignored names
2154
        info.bus == 'input' is used by the HAL version in
2155
        Intrepid for quite different devices like keyboards, mice,
2156
        special laptop switches and buttons, sometimes with odd
2157
        product names like "Video Bus".
2158
2159
        info.bus == 'misc' and info.bus == 'unknown' are obviously
2160
        not very useful, except for the computer itself, which has
2161
        the bus 'unknown'.
2162
2163
        info.bus in ('mmc', 'mmc_host') is used for SD/MMC cards resp.
2164
        the "output aspect" of card readers. We do not not have at
2165
        present enough background information to properly extract a
2166
        vendor and product ID from these cards.
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2167
2168
        info.bus == 'platform' is used for devices like the i8042
2169
        which controls keyboard and mouse; HAL has no vendor
2170
        information for these devices, so there is no point to
2171
        treat them as real devices.
2172
2173
        info.bus == 'pnp' is used for components like the ancient
2174
        AT DMA controller or the keyboard. Like for the bus
2175
        'platform', HAL does not provide any vendor data.
2176
7598.1.1 by Abel Deuring
several HAL bus/subsystem names added to the the set of ignored names
2177
        info.bus == 'power_supply' is used by the HAL version in
2178
        Intrepid for AC adapters an laptop batteries. We don't have
2179
        at present enough information about possible problems with
2180
        missing vendor/product information in order to store the
2181
        data reliably in the HWDB.
7250.1.1 by Abel Deuring
ignore HAL devives with buses 'misc' and 'unkown' in HWDB processing
2182
9710.4.3 by Abel Deuring
BaseDevice.has_reliable_data modified for UdevDevice; tests for Udevdevice.has_reliable_data
2183
        raw_bus == 'acpi' is used in udev data for the main system,
2184
        for CPUs, power supply etc. Except for the main sytsem, none
2185
        of them provides a vendor or product id, so we ignore them.
2186
9747.1.10 by Abel Deuring
Added a number of buses that should treated as being 'unreal' or 'unprocessible' to BaseDevice.is_real_device and BaseDevice.has_reliable_data.
2187
        raw_bus == 'video_output', 'thermal', 'vtconsole', 'bdi',
2188
        'mem', 'ppp', 'vc', 'dmi', 'hidraw', 'hwmon', 'heci', 'rfkill',
9830.7.1 by Abel Deuring
hwdbsubmissions.py: Extended the sets of bus names known to belong to virtual devices or used for nodes that describe only aspects of real devices
2189
        'i2c-adapter', 'ttm', 'ppdev', 'printer', 'cardman_4040', 'msr',
2190
        'ieee1394_protocol', 'dahdi', 'atm', 'asus_oled', 'pktcdvd' is
2191
        used in submissions with udev data for virtual devices.
9747.1.10 by Abel Deuring
Added a number of buses that should treated as being 'unreal' or 'unprocessible' to BaseDevice.is_real_device and BaseDevice.has_reliable_data.
2192
2193
        'pci_bus' is used in submissions with udev data for a node
2194
        describing a PCI bus.
2195
2196
        'leds' is used in submissions with udev data to describe LEDs.
2197
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2198
        XXX Abel Deuring 2008-05-06: IEEE1394 devices are a bit
2199
        nasty: The standard does not define any specification
2200
        for product IDs or product names, hence HAL often uses
2201
        the value 0 for the property ieee1394.product_id and a
2202
        value like "Unknown (0x00d04b)" for ieee.product, where
2203
        0x00d04b is the vendor ID. I have currently no idea how
2204
        to find or generate something that could be used as the
2205
        product ID, so IEEE1394 devices are at present simply
2206
        not stored in the HWDB. Otherwise, we'd pollute the HWDB
6533.1.2 by Abel Deuring
implemented reviewer's comments
2207
        with unreliable data. Bug #237044.
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2208
2209
        While PCMCIA devices have a manufacturer ID, at least its
2210
        value as provided by HAL in pcmcia.manf_id it is not very
2211
        reliable. The HAL property pcmcia.prod_id1 is too not
2212
        reliable. Sometimes it contains a useful vendor name like
2213
        "O2Micro" or "ATMEL", but sometimes useless values like
2214
        "IEEE 802.11b". See for example
2215
        drivers/net/wireless/atmel_cs.c in the Linux kernel sources.
7233.1.1 by Abel Deuring
Processing HWDB submissions containing IDE devices without vendor and product ID causes no longer log messages about missing vendor/product ID
2216
2217
        Provided that a device is not excluded by the above criteria,
2218
        ensure that we have vendor ID, product ID and product name.
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2219
        """
7250.1.1 by Abel Deuring
ignore HAL devives with buses 'misc' and 'unkown' in HWDB processing
2220
        bus = self.raw_bus
9710.4.3 by Abel Deuring
BaseDevice.has_reliable_data modified for UdevDevice; tests for Udevdevice.has_reliable_data
2221
        if bus in ('unknown', 'acpi') and not self.is_root_device:
7250.1.1 by Abel Deuring
ignore HAL devives with buses 'misc' and 'unkown' in HWDB processing
2222
            # The root node is course a real device; storing data
2223
            # about other devices with the bus "unkown" is pointless.
2224
            return False
9830.7.1 by Abel Deuring
hwdbsubmissions.py: Extended the sets of bus names known to belong to virtual devices or used for nodes that describe only aspects of real devices
2225
        if bus in ('asus_oled', 'atm', 'backlight', 'bdi', 'bluetooth',
2226
                    'cardman_4040', 'dahdi', 'dmi', 'heci', 'hidraw', 'hwmon',
2227
                   'i2c-adapter', 'ieee1394', 'ieee1394_protocol', 'input',
2228
                   'leds', 'mem', 'misc', 'mmc', 'mmc_host', 'msr', 'pci_bus',
2229
                   'pcmcia', 'pktcdvd', 'platform', 'pnp', 'power_supply',
2230
                   'ppdev', 'ppp', 'printer', 'rfkill', 'thermal', 'ttm',
2231
                   'vc', 'video_output', 'vtconsole'):
7233.1.1 by Abel Deuring
Processing HWDB submissions containing IDE devices without vendor and product ID causes no longer log messages about missing vendor/product ID
2232
            return False
2233
2234
        # We identify devices by bus, vendor ID and product ID;
2235
        # additionally, we need a product name. If any of these
2236
        # are not available, we can't store information for this
2237
        # device.
2238
        if (self.real_bus is None or self.vendor_id is None
2239
            or self.product_id is None or self.product is None):
2240
            # Many IDE devices don't provide useful vendor and product
2241
            # data. We don't want to clutter the log with warnings
2242
            # about this problem -- there is nothing we can do to fix
2243
            # it.
2244
            if self.real_bus != HWBus.IDE:
2245
                self.parser._logWarning(
9710.4.3 by Abel Deuring
BaseDevice.has_reliable_data modified for UdevDevice; tests for Udevdevice.has_reliable_data
2246
                    'A %s that is supposed to be a real device does '
7233.1.1 by Abel Deuring
Processing HWDB submissions containing IDE devices without vendor and product ID causes no longer log messages about missing vendor/product ID
2247
                    'not provide bus, vendor ID, product ID or product name: '
2248
                    '%r %r %r %r %s'
9710.4.3 by Abel Deuring
BaseDevice.has_reliable_data modified for UdevDevice; tests for Udevdevice.has_reliable_data
2249
                    % (self.__class__.__name__, self.real_bus, self.vendor_id,
2250
                       self.product_id, self.product, self.device_id),
7233.1.1 by Abel Deuring
Processing HWDB submissions containing IDE devices without vendor and product ID causes no longer log messages about missing vendor/product ID
2251
                    self.parser.submission_key)
2252
            return False
2253
        return True
6533.1.1 by Abel Deuring
Implementation of class HALDevice, part 4: moved detection of devices that can't yet be processed from is_real_device into a separate property; added properties for vendor/product ID as needed for HWVendorID/HWDevice; fixed a few bugs in _logWarning calls
2254
7242.2.1 by Abel Deuring
avoid exception when an HWDB submission with an ATA disk with a long product name is processed
2255
    def getScsiVendorAndModelName(self):
7242.2.2 by Abel Deuring
implemented reviewer's comments
2256
        """Separate vendor and model name of SCSI decvices.
7242.2.1 by Abel Deuring
avoid exception when an HWDB submission with an ATA disk with a long product name is processed
2257
2258
        SCSI devcies are identified by an 8 charcter vendor name
2259
        and an 16 character model name. The Linux kernel use the
2260
        the SCSI command set to access block devices connected
2261
        via USB, IEEE1394 and ATA buses too.
2262
2263
        For ATA disks, the Linux kernel sets the vendor name to "ATA"
2264
        and prepends the model name with the real vendor name, but only
2265
        if the combined length if not larger than 16. Otherwise the
2266
        real vendor name is omitted.
2267
2268
        This method provides a safe way to retrieve the  the SCSI vendor
2269
        and model name.
2270
2271
        If the vendor name is 'ATA', and if the model name contains
2272
        at least one ' ' character, the string before the first ' ' is
8037.2.1 by Brad Crittenden
fixed annoying typos
2273
        returned as the vendor name, and the string after the first
7242.2.1 by Abel Deuring
avoid exception when an HWDB submission with an ATA disk with a long product name is processed
2274
        ' ' is returned as the model name.
2275
2276
        In all other cases, vendor and model name are returned unmodified.
2277
        """
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2278
        vendor = self.scsi_vendor
7242.2.1 by Abel Deuring
avoid exception when an HWDB submission with an ATA disk with a long product name is processed
2279
        if vendor == 'ATA':
2280
            # The assumption below that the vendor name does not
2281
            # contain any spaces is not necessarily correct, but
2282
            # it is hard to find a better heuristic to separate
2283
            # the vendor name from the product name.
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2284
            splitted_name = self.scsi_model.split(' ', 1)
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2285
            if len(splitted_name) == 2:
2286
                return {
2287
                    'vendor': splitted_name[0],
2288
                    'product': splitted_name[1],
2289
                    }
2290
        return {
2291
            'vendor': vendor,
2292
            'product': self.scsi_model,
2293
            }
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2294
2295
    def getDriver(self):
2296
        """Return the HWDriver instance associated with this device.
2297
6934.2.2 by Abel Deuring
implemented reviewer's comments
2298
        Create a HWDriver record, if it does not already exist.
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2299
        """
2300
        # HAL and the HWDB client know at present only about kernel
2301
        # drivers, so there is currently no need to search for
6934.2.2 by Abel Deuring
implemented reviewer's comments
2302
        # for user space printer drivers, for example.
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2303
        if self.driver_name is not None:
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2304
            db_driver_set = getUtility(IHWDriverSet)
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2305
            return db_driver_set.getOrCreate(
9747.1.4 by Abel Deuring
changed SubmissionParser.getKernelPackageName into property SubmissionParser.kernel_package_name; added code to kernel_package_name to deal with data from HWDB submissions with ude data.
2306
                self.parser.kernel_package_name, self.driver_name)
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2307
        else:
2308
            return None
2309
6934.2.2 by Abel Deuring
implemented reviewer's comments
2310
    def ensureVendorIDVendorNameExists(self):
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2311
        """Ensure that a useful HWVendorID record for self.vendor_id exists.
2312
2313
        A vendor ID is associated with a vendor name. For many devices
2314
        we rely on the information from the submission to create this
2315
        association in the HWVendorID table.
2316
2317
        We do _not_ use the submitted vendor name for USB, PCI and
2318
        PCCard devices, because we can get them from independent
2319
        sources. See l/c/l/doc/hwdb-device-tables.txt.
2320
        """
7178.2.2 by Abel Deuring
changed method HALDevice.getRealBus() into property HALDevice.real_bus; fixed a typo
2321
        bus = self.real_bus
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2322
        if (self.vendor is not None and
6934.2.2 by Abel Deuring
implemented reviewer's comments
2323
            bus not in (HWBus.PCI, HWBus.PCCARD, HWBus.USB)):
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2324
            hw_vendor_id_set = getUtility(IHWVendorIDSet)
2325
            hw_vendor_id = hw_vendor_id_set.getByBusAndVendorID(
7272.3.1 by Abel Deuring
fixed a bug in HALDevice.ensureVendorIDVendorNameExists
2326
                bus, self.vendor_id_for_db)
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2327
            if hw_vendor_id is None:
2328
                hw_vendor_name_set = getUtility(IHWVendorNameSet)
2329
                hw_vendor_name = hw_vendor_name_set.getByName(self.vendor)
2330
                if hw_vendor_name is None:
2331
                    hw_vendor_name = hw_vendor_name_set.create(self.vendor)
2332
                hw_vendor_id_set.create(
7272.3.1 by Abel Deuring
fixed a bug in HALDevice.ensureVendorIDVendorNameExists
2333
                    self.real_bus, self.vendor_id_for_db, hw_vendor_name)
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2334
2335
    def createDBData(self, submission, parent_submission_device):
2336
        """Create HWDB records for this HAL device and its children.
2337
2338
        A HWDevice record for (bus, vendor ID, product ID) of this
2339
        device and a HWDeviceDriverLink record (device, None) are
2340
        created, if they do not already exist.
2341
2342
        A HWSubmissionDevice record is created for (HWDeviceDriverLink,
2343
        submission).
2344
2345
        HWSubmissionDevice records and missing HWDeviceDriverLink
2346
        records for known drivers of this device are created.
2347
2348
        createDBData is called recursively for all real child devices.
2349
2350
        This method may only be called, if self.real_device == True.
2351
        """
2352
        assert self.is_real_device, ('HALDevice.createDBData must be called '
2353
                                     'for real devices only.')
2354
        if not self.has_reliable_data:
2355
            return
7178.2.2 by Abel Deuring
changed method HALDevice.getRealBus() into property HALDevice.real_bus; fixed a typo
2356
        bus = self.real_bus
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2357
        vendor_id = self.vendor_id_for_db
2358
        product_id = self.product_id_for_db
2359
        product_name = self.product
2360
6934.2.2 by Abel Deuring
implemented reviewer's comments
2361
        self.ensureVendorIDVendorNameExists()
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2362
6934.2.2 by Abel Deuring
implemented reviewer's comments
2363
        db_device = getUtility(IHWDeviceSet).getOrCreate(
2364
            bus, vendor_id, product_id, product_name)
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2365
        # Create a HWDeviceDriverLink record without an associated driver
2366
        # for each real device. This will allow us to relate tests and
2367
        # bugs to a device in general as well as to a specific
2368
        # combination of a device and a driver.
6934.2.2 by Abel Deuring
implemented reviewer's comments
2369
        device_driver_link = getUtility(IHWDeviceDriverLinkSet).getOrCreate(
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2370
            db_device, None)
6934.2.2 by Abel Deuring
implemented reviewer's comments
2371
        submission_device = getUtility(IHWSubmissionDeviceSet).create(
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2372
            device_driver_link, submission, parent_submission_device,
2373
            self.id)
2374
        self.createDBDriverData(submission, db_device, submission_device)
2375
2376
    def createDBDriverData(self, submission, db_device, submission_device):
2377
        """Create HWDB records for drivers of this device and its children.
2378
2379
        This method creates HWDeviceDriverLink and HWSubmissionDevice
2380
        records for this device and its children.
2381
        """
2382
        driver = self.getDriver()
2383
        if driver is not None:
2384
            device_driver_link_set = getUtility(IHWDeviceDriverLinkSet)
2385
            device_driver_link = device_driver_link_set.getOrCreate(
2386
                db_device, driver)
6934.2.2 by Abel Deuring
implemented reviewer's comments
2387
            submission_device = getUtility(IHWSubmissionDeviceSet).create(
6934.2.1 by Abel Deuring
population of HDWB device tables with submission data
2388
                device_driver_link, submission, submission_device, self.id)
2389
        for sub_device in self.children:
2390
            if sub_device.is_real_device:
2391
                sub_device.createDBData(submission, submission_device)
2392
            else:
2393
                sub_device.createDBDriverData(submission, db_device,
2394
                                              submission_device)
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2395
2396
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2397
class HALDevice(BaseDevice):
2398
    """The representation of a HAL device node."""
2399
2400
    def __init__(self, id, udi, properties, parser):
2401
        """HALDevice constructor.
2402
2403
        :param id: The ID of the HAL device in the submission data as
2404
            specified in <device id=...>.
2405
        :type id: int
2406
        :param udi: The UDI of the HAL device.
2407
        :type udi: string
2408
        :param properties: The HAL properties of the device.
2409
        :type properties: dict
2410
        :param parser: The parser processing a submission.
2411
        :type parser: SubmissionParser
2412
        """
2413
        super(HALDevice, self).__init__(parser)
2414
        self.id = id
2415
        self.udi = udi
2416
        self.properties = properties
2417
2418
    def getProperty(self, property_name):
2419
        """Return the HAL property property_name.
2420
2421
        Note that there is no check of the property type.
2422
        """
2423
        if property_name not in self.properties:
2424
            return None
2425
        name, type_ = self.properties[property_name]
2426
        return name
2427
2428
    @property
2429
    def parent_udi(self):
2430
        """The UDI of the parent device."""
2431
        return self.getProperty('info.parent')
2432
2433
    @property
2434
    def device_id(self):
2435
        """See `BaseDevice`."""
2436
        return self.udi
2437
2438
    @property
2439
    def pci_class(self):
2440
        """See `BaseDevice`."""
2441
        return self.getProperty('pci.device_class')
2442
2443
    @property
2444
    def pci_subclass(self):
2445
        """The PCI device sub-class of the device or None for Non-PCI devices.
2446
        """
2447
        return self.getProperty('pci.device_subclass')
2448
2449
    @property
2450
    def usb_vendor_id(self):
2451
        """See `BaseDevice`."""
2452
        return self.getProperty('usb_device.vendor_id')
2453
2454
    @property
2455
    def usb_product_id(self):
2456
        """See `BaseDevice`."""
2457
        return self.getProperty('usb_device.product_id')
2458
2459
    @property
2460
    def scsi_vendor(self):
2461
        """See `BaseDevice`."""
2462
        return self.getProperty('scsi.vendor')
2463
2464
    @property
2465
    def scsi_model(self):
2466
        """See `BaseDevice`."""
2467
        return self.getProperty('scsi.model')
2468
2469
    @property
2470
    def driver_name(self):
2471
        """See `BaseDevice`."""
2472
        return self.getProperty('info.linux.driver')
2473
2474
    @property
2475
    def raw_bus(self):
2476
        """See `BaseDevice`."""
2477
        # Older versions of HAL stored this value in the property
2478
        # info.bus; newer versions store it in info.subsystem.
2479
        #
2480
        # Note that info.bus is gone for all devices except the
2481
        # USB bus. For USB devices, the property info.bus returns more
2482
        # detailed data: info.subsystem has the value 'usb' for all
2483
        # HAL nodes belonging to USB devices, while info.bus has the
2484
        # value 'usb_device' for the root node of a USB device, and the
2485
        # value 'usb' for sub-nodes of a USB device. We use these
2486
        # different value to to find the root USB device node, hence
2487
        # try to read info.bus first.
2488
        result = self.getProperty('info.bus')
2489
        if result is not None:
2490
            return result
2491
        return self.getProperty('info.subsystem')
2492
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
2493
    @property
2494
    def is_root_device(self):
2495
        """See `BaseDevice`."""
2496
        return self.udi == ROOT_UDI
2497
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2498
    def getVendorOrProduct(self, type_):
2499
        """Return the vendor or product of this device.
2500
2501
        :return: The vendor or product data for this device.
2502
        :param type_: 'vendor' or 'product'
2503
        """
2504
        # HAL does not store vendor data very consistently. Try to find
2505
        # the data in several places.
2506
        assert type_ in ('vendor', 'product'), (
2507
            'Unexpected value of type_: %r' % type_)
2508
2509
        bus = self.raw_bus
2510
        if self.udi == ROOT_UDI:
2511
            # HAL sets info.product to "Computer", provides no property
2512
            # info.vendor and raw_bus is "unknown", hence the logic
2513
            # below does not work properly.
2514
            return self.getProperty('system.hardware.' + type_)
2515
        elif bus == 'scsi':
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2516
            return self.getScsiVendorAndModelName()[type_]
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2517
        else:
2518
            result = self.getProperty('info.' + type_)
2519
            if result is None:
2520
                if bus is None:
2521
                    return None
2522
                else:
2523
                    return self.getProperty('%s.%s' % (bus, type_))
2524
            else:
2525
                return result
2526
2527
    @property
2528
    def vendor(self):
2529
        """See `BaseDevice`."""
2530
        return self.getVendorOrProduct('vendor')
2531
2532
    @property
2533
    def product(self):
2534
        """See `BaseDevice`."""
2535
        return self.getVendorOrProduct('product')
2536
2537
    def getVendorOrProductID(self, type_):
2538
        """Return the vendor or product ID for this device.
2539
2540
        :return: The vendor or product ID for this device.
2541
        :param type_: 'vendor' or 'product'
2542
        """
2543
        assert type_ in ('vendor', 'product'), (
2544
            'Unexpected value of type_: %r' % type_)
2545
        bus = self.raw_bus
2546
        if self.udi == ROOT_UDI:
2547
            # HAL does not provide IDs for a system itself, we use the
2548
            # vendor resp. product name instead.
2549
            return self.getVendorOrProduct(type_)
2550
        elif bus is None:
2551
            return None
2552
        elif bus == 'scsi' or self.udi == ROOT_UDI:
2553
            # The SCSI specification does not distinguish between a
2554
            # vendor/model ID and vendor/model name: the SCSI INQUIRY
2555
            # command returns an 8 byte string as the vendor name and
2556
            # a 16 byte string as the model name. We use these strings
2557
            # as the vendor/product name as well as the vendor/product
2558
            # ID.
2559
            #
2560
            # Similary, HAL does not provide a vendor or product ID
2561
            # for the host system itself, so we use the vendor resp.
2562
            # product name as the vendor/product ID for systems too.
2563
            return self.getVendorOrProduct(type_)
2564
        else:
2565
            return self.getProperty('%s.%s_id' % (bus, type_))
2566
2567
    @property
2568
    def vendor_id(self):
2569
        """See `BaseDevice`."""
2570
        return self.getVendorOrProductID('vendor')
2571
2572
    @property
2573
    def product_id(self):
2574
        """See `BaseDevice`."""
2575
        return self.getVendorOrProductID('product')
2576
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
2577
    @property
2578
    def scsi_controller(self):
2579
        """See `BaseDevice`."""
2580
        # While SCSI devices from valid submissions should have a
2581
        # parent and a grandparent, we can't be sure for bogus or
2582
        # broken submissions.
2583
        if self.raw_bus != 'scsi':
2584
            return None
2585
        parent = self.parent
2586
        if parent is None:
2587
            self.parser._logWarning(
2588
                'Found SCSI device without a parent: %s.' % self.device_id)
2589
            return None
2590
        grandparent = parent.parent
2591
        if grandparent is None:
2592
            self.parser._logWarning(
2593
                'Found SCSI device without a grandparent: %s.'
2594
                % self.device_id)
2595
            return None
2596
        return grandparent
2597
9600.1.5 by Abel Deuring
Refactoring of l.c.l.scripts.hwdbsubmissions.HALDevice, part 3
2598
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2599
class UdevDevice(BaseDevice):
2600
    """The representation of a udev device node."""
2601
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2602
    def __init__(self, parser, udev_data, sysfs_data=None, dmi_data=None):
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2603
        """HALDevice constructor.
2604
2605
        :param udevdata: The udev data for this device
2606
        :param sysfs_data: sysfs data for this device.
2607
        :param parser: The parser processing a submission.
2608
        :type parser: SubmissionParser
2609
        """
2610
        super(UdevDevice, self).__init__(parser)
2611
        self.udev = udev_data
2612
        self.sysfs = sysfs_data
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2613
        self.dmi = dmi_data
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2614
2615
    @property
2616
    def device_id(self):
2617
        """See `BaseDevice`."""
2618
        return self.udev['P']
2619
2620
    @property
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2621
    def root_device_ids(self):
9685.1.3 by Abel Deuring
fixed a typo
2622
        """The vendor and product IDs of the root device."""
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2623
        return {
2624
            'vendor': self.dmi.get('/sys/class/dmi/id/sys_vendor'),
2625
            'product': self.dmi.get('/sys/class/dmi/id/product_name')
2626
            }
2627
2628
    @property
9644.3.6 by Abel Deuring
implemented reviewer's comments
2629
    def is_pci(self):
2630
        """True, if this is a PCI device, else False."""
2631
        return self.udev['E'].get('SUBSYSTEM') == 'pci'
2632
2633
    @property
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2634
    def pci_class_info(self):
2635
        """Parse the udev property PCI_SUBSYS_ID.
2636
2637
        :return: (PCI class, PCI sub-class, version) for a PCI device
2638
            or (None, None, None) for other devices.
2639
        """
9644.3.6 by Abel Deuring
implemented reviewer's comments
2640
        if self.is_pci:
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2641
            # SubmissionParser.checkConsistentUdevDeviceData() ensures
2642
            # that PCI_CLASS is a 24 bit integer in hexadecimal
2643
            # representation.
9644.3.6 by Abel Deuring
implemented reviewer's comments
2644
            # Bits 16..23 of the number are the man PCI class,
2645
            # bits 8..15 are the sub-class, bits 0..7 are the version.
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2646
            class_info = int(self.udev['E']['PCI_CLASS'], 16)
2647
            return (class_info >> 16, (class_info >> 8) & 0xFF,
2648
                    class_info & 0xFF)
2649
        else:
2650
            return (None, None, None)
2651
2652
    @property
2653
    def pci_class(self):
2654
        """See `BaseDevice`."""
2655
        return self.pci_class_info[0]
2656
2657
    @property
2658
    def pci_subclass(self):
2659
        """See `BaseDevice`."""
2660
        return self.pci_class_info[1]
2661
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2662
    @property
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2663
    def pci_ids(self):
2664
        """The PCI vendor and product IDs.
2665
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2666
        :return: A dictionary containing the vendor and product IDs.
2667
            The IDs are set to None for Non-PCI devices.
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2668
        """
2669
        if self.is_pci:
2670
            # SubmissionParser.checkUdevPciProperties() ensures that
2671
            # each PCI device has the property PCI_ID and that is
2672
            # consists of two 4-digit hexadecimal numbers, separated
2673
            # by a ':'.
2674
            id_string = self.udev['E']['PCI_ID']
2675
            ids = id_string.split(':')
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2676
            return {
2677
                'vendor': int(ids[0], 16),
2678
                'product': int(ids[1], 16),
2679
                }
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2680
        else:
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2681
            return  {
2682
                'vendor': None,
2683
                'product': None,
2684
                }
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2685
2686
    @property
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2687
    def is_usb(self):
2688
        """True, if this is a USB device, else False."""
2689
        return self.udev['E'].get('SUBSYSTEM') == 'usb'
2690
2691
    @property
2692
    def usb_ids(self):
2693
        """The vendor ID, product ID, product version for USB devices.
2694
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2695
        :return: A dictionary containing the vendor and product IDs and
2696
            the product version for USB devices.
2697
            The IDs are set to None for Non-USB devices.
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2698
        """
2699
        if self.is_usb:
2700
            # udev represents USB device IDs as strings
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
2701
            # vendor_id/product_id/version, where each part is
2702
            # a hexadecimal number.
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2703
            # SubmissionParser.checkUdevUsbProperties() ensures that
2704
            # the string PRODUCT is in the format required below.
2705
            product_info = self.udev['E']['PRODUCT'].split('/')
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2706
            return {
2707
                'vendor': int(product_info[0], 16),
2708
                'product': int(product_info[1], 16),
2709
                'version': int(product_info[2], 16),
2710
                }
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2711
        else:
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2712
            return {
2713
                'vendor': None,
2714
                'product': None,
2715
                'version': None,
2716
                }
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2717
2718
    @property
2719
    def usb_vendor_id(self):
2720
        """See `BaseDevice`."""
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2721
        return self.usb_ids['vendor']
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2722
2723
    @property
2724
    def usb_product_id(self):
2725
        """See `BaseDevice`."""
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2726
        return self.usb_ids['product']
9644.3.7 by Abel Deuring
sanity checks for USB data in HWDB submissions with udev as the data source; USB-related properties for class UdevDevice
2727
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
2728
    @property
2729
    def is_scsi_device(self):
9644.3.10 by Abel Deuring
mplemented reveiwer's comments
2730
        """True, if this is a SCSI device, else False."""
9644.3.9 by Abel Deuring
sanity checks for SCSI and DMI data in HWDB subimissions with udev as the data source; SCSI-related properties for class UdevDevice
2731
        # udev sets the property SUBSYSTEM to "scsi" for a number of
2732
        # different nodes: SCSI hosts, SCSI targets and SCSI devices.
2733
        # They are distiguished by the property DEVTYPE.
2734
        properties = self.udev['E']
2735
        return (properties.get('SUBSYSTEM') == 'scsi' and
2736
                properties.get('DEVTYPE') == 'scsi_device')
2737
2738
    @property
2739
    def scsi_vendor(self):
2740
        """The SCSI vendor name of the device or None for Non-SCSI devices."""
2741
        if self.is_scsi_device:
2742
            # SubmissionParser.checkUdevScsiProperties() ensures that
2743
            # each SCSI device has a record in self.sysfs and that
2744
            # the attribute 'vendor' exists.
2745
            path = self.udev['P']
2746
            return self.sysfs['vendor']
2747
        else:
2748
            return None
2749
2750
    @property
2751
    def scsi_model(self):
2752
        """The SCSI model name of the device or None for Non-SCSI devices."""
2753
        if self.is_scsi_device:
2754
            # SubmissionParser.checkUdevScsiProperties() ensures that
2755
            # each SCSI device has a record in self.sysfs and that
2756
            # the attribute 'model' exists.
2757
            path = self.udev['P']
2758
            return self.sysfs['model']
2759
        else:
2760
            return None
2761
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2762
    @property
2763
    def raw_bus(self):
2764
        """See `BaseDevice`."""
2765
        # udev specifies the property SUBSYSTEM for most devices;
2766
        # some devices have additionally the more specific property
2767
        # DEVTYPE. DEVTYPE is preferable.
2768
        # The root device has the subsystem/bus value "acpi", which
2769
        # is a bit nonsensical.
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
2770
        if self.is_root_device:
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2771
            return None
2772
        properties = self.udev['E']
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2773
        devtype = properties.get('DEVTYPE')
2774
        if devtype is not None:
2775
            return devtype
9710.4.2 by Abel Deuring
Adjusted BaseDevice.is_real_device for HWDB submissions with udev data; tests for this property with udev data
2776
        subsystem = properties.get('SUBSYSTEM')
2777
        # A real mess: The main node of a SCSI device has
2778
        # SUBSYSTEM = 'scsi' and DEVTYPE = 'scsi_device', while
2779
        # a sub-node has SUBSYSTEM='scsi_device'. We don't want
2780
        # the two to be confused. The latter node is not of any
2781
        # interest for us, so we return None. This ensures that
2782
        # is_real_device returns False for the sub-node.
2783
        if subsystem != 'scsi_device':
2784
            return subsystem
2785
        else:
2786
            return None
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2787
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
2788
    @property
2789
    def is_root_device(self):
2790
        """See `BaseDevice`."""
2791
        return self.udev['P'] == UDEV_ROOT_PATH
2792
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2793
    def getVendorOrProduct(self, type_):
2794
        """Return the vendor or product of this device.
2795
2796
        :return: The vendor or product data for this device.
2797
        :param type_: 'vendor' or 'product'
2798
        """
2799
        assert type_ in ('vendor', 'product'), (
2800
            'Unexpected value of type_: %r' % type_)
2801
2802
        bus = self.raw_bus
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
2803
        if self.is_root_device:
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2804
            # udev does not known about any product information for
2805
            # the root device. We use DMI data instead.
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2806
            return self.root_device_ids[type_]
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2807
        elif bus == 'scsi_device':
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2808
            return self.getScsiVendorAndModelName()[type_]
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2809
        elif bus in ('pci', 'usb_device'):
2810
            # XXX Abel Deuring 2009-10-13, bug 450480: udev does not
2811
            # provide human-readable vendor and product names for
2812
            # USB and PCI devices. We should retrieve these from
2813
            # http://www.linux-usb.org/usb.ids and
2814
            # http://pciids.sourceforge.net/v2.2/pci.ids
2815
            return 'Unknown'
2816
        else:
2817
            # We don't process yet other devices than complete systems,
2818
            # PCI, USB devices and those devices that are represented
2819
            # in udev as SCSI devices: real SCSI devices, and
2820
            # IDE/ATA/SATA devices.
2821
            return None
2822
2823
    @property
2824
    def vendor(self):
2825
        """See `BaseDevice`."""
2826
        return self.getVendorOrProduct('vendor')
2827
2828
    @property
2829
    def product(self):
2830
        """See `BaseDevice`."""
2831
        return self.getVendorOrProduct('product')
2832
2833
    def getVendorOrProductID(self, type_):
2834
        """Return the vendor or product ID of this device.
2835
2836
        :return: The vendor or product ID for this device.
2837
        :param type_: 'vendor' or 'product'
2838
        """
2839
        assert type_ in ('vendor', 'product'), (
2840
            'Unexpected value of type_: %r' % type_)
2841
2842
        bus = self.raw_bus
9691.1.4 by Abel Deuring
fixed test data for Udev USB storage device; new property is_root_device for class BaseDevice; adapted BaseDevice.real_device for UdevDevice; tests for BaseDevice.real_device and for BaseDevice.translatePciBus() with udev data
2843
        if self.is_root_device:
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2844
            # udev does not known about any product information for
2845
            # the root device. We use DMI data instead.
2846
            if type_ == 'vendor':
2847
                return self.dmi.get('/sys/class/dmi/id/sys_vendor')
2848
            else:
2849
                return self.dmi.get('/sys/class/dmi/id/product_name')
2850
        elif bus == 'scsi_device':
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2851
            return self.getScsiVendorAndModelName()[type_]
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2852
        elif bus == 'pci':
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2853
            return self.pci_ids[type_]
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2854
        elif bus == 'usb_device':
9685.1.2 by Abel Deuring
implemented reveiwer's comments
2855
            return self.usb_ids[type_]
9685.1.1 by Abel Deuring
properties pci_ids, raw_bus, vendor, vendor_id, product, product_id for class UdevDevice
2856
        else:
2857
            # We don't process yet other devices than complete systems,
2858
            # PCI, USB devices and those devices that are represented
2859
            # in udev as SCSI devices: real SCSI devices, and
2860
            # IDE/ATA/SATA devices.
2861
            return None
2862
2863
    @property
2864
    def vendor_id(self):
2865
        """See `BaseDevice`."""
2866
        return self.getVendorOrProductID('vendor')
2867
2868
    @property
2869
    def product_id(self):
2870
        """See `BaseDevice`."""
2871
        return self.getVendorOrProductID('product')
2872
9691.1.1 by Abel Deuring
properties UdevDevice.vendor_id_for_db, UdevDevice.product_id_for_db fixed; new property Udev.driver_name; refactored BaseDevice.translateScsiBus(); new property BaseDevice.scsi_controller
2873
    @property
2874
    def driver_name(self):
2875
        """See `BaseDevice`."""
2876
        return self.udev['E'].get('DRIVER')
2877
9691.1.2 by Abel Deuring
Property UdevDevcie.scsi_controller; tests for UdevDevice.translateScsiBus()
2878
    @property
2879
    def scsi_controller(self):
2880
        """See `BaseDevice`."""
2881
        if self.raw_bus != 'scsi_device':
2882
            return None
2883
9691.1.3 by Abel Deuring
implemented reviewar's comments
2884
        # While SCSI devices from valid submissions should have four
2885
        # ancestors, we can't be sure for bogus or broken submissions.
2886
        try:
9710.4.1 by Abel Deuring
serious bug in UdevDevice.scsi_controller fixed; TestUdevDevice.buildUdevDeviceHierarchy() now builds the parent-child relations based on the device paths and returns a dict instead of a list. Affected tests updated
2887
            controller = self.parent.parent.parent
9691.1.3 by Abel Deuring
implemented reviewar's comments
2888
        except AttributeError:
2889
            controller = None
2890
        if controller is None:
2891
            self.parser._logWarning(
2892
                'Found a SCSI device without a sufficient number of '
2893
                'ancestors: %s' % self.device_id)
2894
            return None
2895
        return controller
9644.3.1 by Abel Deuring
more sanity checks for HWDB submissions with udev data; new class UdevDevice defiend
2896
9747.1.1 by Abel Deuring
Property UdevDevice.id added
2897
    @property
2898
    def id(self):
2899
        return self.udev['id']
2900
2901
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2902
class ProcessingLoop(object):
2903
    """An `ITunableLoop` for processing HWDB submissions."""
2904
2905
    implements(ITunableLoop)
2906
2907
    def __init__(self, transaction, logger, max_submissions):
2908
        self.transaction = transaction
2909
        self.logger = logger
2910
        self.max_submissions = max_submissions
2911
        self.valid_submissions = 0
2912
        self.invalid_submissions = 0
7123.1.2 by Abel Deuring
implemented reviewer's comments
2913
        self.finished = False
7508.1.1 by Abel Deuring
fix for bug 314342: HWDB submission processing script does not process private submissions
2914
        self.janitor = getUtility(ILaunchpadCelebrities).janitor
7123.1.2 by Abel Deuring
implemented reviewer's comments
2915
2916
    def _validateSubmission(self, submission):
2917
        submission.status = HWSubmissionProcessingStatus.PROCESSED
2918
        self.valid_submissions += 1
2919
2920
    def _invalidateSubmission(self, submission):
2921
        submission.status = HWSubmissionProcessingStatus.INVALID
2922
        self.invalid_submissions += 1
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2923
2924
    def isDone(self):
2925
        """See `ITunableLoop`."""
7123.1.2 by Abel Deuring
implemented reviewer's comments
2926
        return self.finished
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2927
7881.6.1 by Abel Deuring
Better handling of errors in the HWDB submission processing script that are caused by a non-responding Librarian
2928
    def reportOops(self, error_explanation):
2929
        """Create an OOPS report and the OOPS ID."""
2930
        info = sys.exc_info()
2931
        properties = [('error-explanation', error_explanation)]
2932
        request = ScriptRequest(properties)
2933
        error_utility = ErrorReportingUtility()
2934
        error_utility.raising(info, request)
2935
        self.logger.error('%s (%s)' % (error_explanation, request.oopsid))
2936
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2937
    def __call__(self, chunk_size):
2938
        """Process a batch of yet unprocessed HWDB submissions."""
7123.1.1 by Abel Deuring
two embarassing bugs in the loop management of the HWDB processing script fixed
2939
        # chunk_size is a float; we compare it below with an int value,
7123.1.2 by Abel Deuring
implemented reviewer's comments
2940
        # which can lead to unexpected results. Since it is also used as
7123.1.3 by Abel Deuring
implenented reviewer's comments
2941
        # a limit for an SQL query, convert it into an integer.
7123.1.1 by Abel Deuring
two embarassing bugs in the loop management of the HWDB processing script fixed
2942
        chunk_size = int(chunk_size)
2943
        submissions = getUtility(IHWSubmissionSet).getByStatus(
7508.1.1 by Abel Deuring
fix for bug 314342: HWDB submission processing script does not process private submissions
2944
            HWSubmissionProcessingStatus.SUBMITTED,
2945
            user=self.janitor
2946
            )[:chunk_size]
7554.1.1 by Bjorn Tillenius
get rid of a COUNT query.
2947
        # Listify the submissions, since we'll have to loop over each
2948
        # one anyway. This saves a COUNT query for getting the number of
2949
        # submissions
2950
        submissions = list(submissions)
2951
        if len(submissions) < chunk_size:
7123.1.2 by Abel Deuring
implemented reviewer's comments
2952
            self.finished = True
7881.6.1 by Abel Deuring
Better handling of errors in the HWDB submission processing script that are caused by a non-responding Librarian
2953
2954
        # Note that we must either change the status of each submission
2955
        # in the loop below or we must abort the submission processing
2956
        # entirely: getUtility(IHWSubmissionSet).getByStatus() above
2957
        # returns the oldest submissions first, so if one submission
2958
        # would remain in the status SUBMITTED, it would be returned
2959
        # in the next loop run again, leading to a potentially endless
2960
        # loop.
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2961
        for submission in submissions:
2962
            try:
2963
                parser = SubmissionParser(self.logger)
2964
                success = parser.processSubmission(submission)
2965
                if success:
7123.1.2 by Abel Deuring
implemented reviewer's comments
2966
                    self._validateSubmission(submission)
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2967
                else:
7123.1.2 by Abel Deuring
implemented reviewer's comments
2968
                    self._invalidateSubmission(submission)
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2969
            except (KeyboardInterrupt, SystemExit):
2970
                # We should never catch these exceptions.
2971
                raise
7881.6.1 by Abel Deuring
Better handling of errors in the HWDB submission processing script that are caused by a non-responding Librarian
2972
            except LibrarianServerError, error:
7881.6.4 by Abel Deuring
re-implementation of handling Librarian errors in scripts/hwdbsubmissions.py, based on better error handling in FileDownloadClient.getFileByAlias().
2973
                # LibrarianServerError is raised when the server could
2974
                # not be reaches for 30 minutes.
2975
                #
7881.6.1 by Abel Deuring
Better handling of errors in the HWDB submission processing script that are caused by a non-responding Librarian
2976
                # In this case we can neither validate nor invalidate the
2977
                # submission. Moreover, the attempt to process the next
2978
                # submission will most likely also fail, so we should give
2979
                # up for now.
7881.6.4 by Abel Deuring
re-implementation of handling Librarian errors in scripts/hwdbsubmissions.py, based on better error handling in FileDownloadClient.getFileByAlias().
2980
                #
7881.6.1 by Abel Deuring
Better handling of errors in the HWDB submission processing script that are caused by a non-responding Librarian
2981
                # This exception is raised before any data for the current
2982
                # submission is processed, hence we can commit submissions
2983
                # processed in previous runs of this loop without causing
2984
                # any inconsistencies.
2985
                self.transaction.commit()
2986
2987
                self.reportOops(
2988
                    'Could not reach the Librarian while processing HWDB '
2989
                    'submission %s' % submission.submission_key)
2990
                raise
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2991
            except Exception, error:
7881.6.1 by Abel Deuring
Better handling of errors in the HWDB submission processing script that are caused by a non-responding Librarian
2992
                self.transaction.abort()
2993
                self.reportOops(
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
2994
                    'Exception while processing HWDB submission %s'
2995
                    % submission.submission_key)
2996
7123.1.2 by Abel Deuring
implemented reviewer's comments
2997
                self._invalidateSubmission(submission)
7123.1.1 by Abel Deuring
two embarassing bugs in the loop management of the HWDB processing script fixed
2998
                # Ensure that this submission is marked as bad, even if
2999
                # further submissions in this batch raise an exception.
3000
                self.transaction.commit()
3001
3002
            if self.max_submissions is not None:
3003
                if self.max_submissions <= (
3004
                    self.valid_submissions + self.invalid_submissions):
7123.1.2 by Abel Deuring
implemented reviewer's comments
3005
                    self.finished = True
7123.1.1 by Abel Deuring
two embarassing bugs in the loop management of the HWDB processing script fixed
3006
                    break
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
3007
        self.transaction.commit()
3008
3009
def process_pending_submissions(transaction, logger, max_submissions=None):
3010
    """Process pending submissions.
3011
3012
    Parse pending submissions, store extracted data in HWDB tables and
3013
    mark them as either PROCESSED or INVALID.
3014
    """
3015
    loop = ProcessingLoop(transaction, logger, max_submissions)
7123.1.3 by Abel Deuring
implenented reviewer's comments
3016
    # It is hard to predict how long it will take to parse a submission.
3017
    # we don't want to last a DB transaction too long but we also
3018
    # don't want to commit more often than necessary. The LoopTuner
3019
    # handles this for us. The loop's run time will be approximated to
3020
    # 2 seconds, but will never handle more than 50 submissions.
7123.1.1 by Abel Deuring
two embarassing bugs in the loop management of the HWDB processing script fixed
3021
    loop_tuner = LoopTuner(
3022
                loop, 2, minimum_chunk_size=1, maximum_chunk_size=50)
6978.2.7 by Abel Deuring
method added to class SubmissionParser that ties together all processing steps, ITunableLoop added for batch processing; more test data added
3023
    loop_tuner.run()
3024
    logger.info(
3025
        'Processed %i valid and %i invalid HWDB submissions'
3026
        % (loop.valid_submissions, loop.invalid_submissions))