Merge lp:~adeuring/launchpad/hwdb-class-udev-device-6 into lp:launchpad
- hwdb-class-udev-device-6
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~adeuring/launchpad/hwdb-class-udev-device-6 |
Merge into: | lp:launchpad |
Diff against target: |
849 lines 2 files modified
lib/canonical/launchpad/scripts/hwdbsubmissions.py (+61/-20) lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py (+582/-70) |
To merge this branch: | bzr merge lp:~adeuring/launchpad/hwdb-class-udev-device-6 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Nelson (community) | code | Approve | |
Review via email: mp+13402@code.launchpad.net |
Commit message
Description of the change
Abel Deuring (adeuring) wrote : | # |
Michael Nelson (michael.nelson) wrote : | # |
> This branch adds a property scsi_controller to class UdevDevice in the script
> hwdbsubmissons.py.
Hi Abel. Again, thanks for the details about this branch - it's very helpful to have such a clear explanation when the topic is quite foreign (at least, to me!).
I didn't have anything other than a few questions (see below). Great work!
[snip]
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
[snip]
> @@ -2617,6 +2635,29 @@
> """See `BaseDevice`."""
> return self.getVendorO
>
> + @property
> + def driver_name(self):
> + """See `BaseDevice`."""
> + return self.udev[
> +
> + @property
> + def scsi_controller
> + """See `BaseDevice`."""
> + if self.raw_bus != 'scsi_device':
> + return None
> +
> + current_device = self
> + for ancestor_level in range(1, 5):
> + if current_
> + # While SCSI devices from valid submissions should have a
> + # parent and a grandparent, we can't be sure for bogus or
> + # broken submissions.
> + self.parser.
> + 'Found a SCSI device without a sufficient number of '
> + 'ancestors: %s' % self.device_id)
> + return None
> + current_device = current_
> + return current_device
Nice!
Actually, if you're just presenting a general log warning, I wonder whether
doing something like the following would be more readable?
try:
controller = self.parent.
except AttributeError:
return None
Up to you.
>
> class ProcessingLoop(
> """An `ITunableLoop` for processing HWDB submissions."""
>
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -539,6 +539,214 @@
> 'HAL property info.bus.')
>
>
> + def test_HALDevice_
> + """test of HALDevice.
> +
> + The physical device is a USB storage device.
> + """
> + devices = [
> + # The main node of the USB storage device.
> + {
> + 'id': 1,
> + 'udi': self.UDI_
> + 'properties': {
> + 'info.bus': ('usb_device', 'str'),
> + },
> + },
Just a style question (the answer to which I'm not certain), based on:
https:/
should this be:
devices = [{
# The main node of the USB storage device.
'id': 1,
'udi': self.UDI_
'...
Abel Deuring (adeuring) wrote : | # |
Hi Michael,
thanks for your review!
On 15.10.2009 13:54, Michael Nelson wrote:
> Review: Approve code
>> This branch adds a property scsi_controller to class UdevDevice in the script
>> hwdbsubmissons.py.
>
> Hi Abel. Again, thanks for the details about this branch - it's very helpful to have such a clear explanation when the topic is quite foreign (at least, to me!).
>
> I didn't have anything other than a few questions (see below). Great work!
>
> [snip]
>
>> === modified file 'lib/canonical/
>> --- lib/canonical/
>> +++ lib/canonical/
> [snip]
>> @@ -2617,6 +2635,29 @@
>> """See `BaseDevice`."""
>> return self.getVendorO
>>
>> + @property
>> + def driver_name(self):
>> + """See `BaseDevice`."""
>> + return self.udev[
>> +
>> + @property
>> + def scsi_controller
>> + """See `BaseDevice`."""
>> + if self.raw_bus != 'scsi_device':
>> + return None
>> +
>> + current_device = self
>> + for ancestor_level in range(1, 5):
>> + if current_
>> + # While SCSI devices from valid submissions should have a
>> + # parent and a grandparent, we can't be sure for bogus or
>> + # broken submissions.
>> + self.parser.
>> + 'Found a SCSI device without a sufficient number of '
>> + 'ancestors: %s' % self.device_id)
>> + return None
>> + current_device = current_
>> + return current_device
>
> Nice!
>
> Actually, if you're just presenting a general log warning, I wonder whether
> doing something like the following would be more readable?
> try:
> controller = self.parent.
> except AttributeError:
> return None
>
> Up to you.
Right, that's a bit simpler and beter eadable. Changed.
>
>>
>> class ProcessingLoop(
>> """An `ITunableLoop` for processing HWDB submissions."""
>>
>> === modified file 'lib/canonical/
>> --- lib/canonical/
>> +++ lib/canonical/
>> @@ -539,6 +539,214 @@
>> 'HAL property info.bus.')
>>
>>
>> + def test_HALDevice_
>> + """test of HALDevice.
>> +
>> + The physical device is a USB storage device.
>> + """
>> + devices = [
>> + # The main node of the USB storage device.
>> + {
>> + 'id': 1,
>> + 'udi': self.UDI_
>> + 'properties': {
>> + 'info.bus': ('usb_device', 'str'),
>> + },
>> + },
>
> Just a style question (the answer to whic...
Preview Diff
1 | === modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py' |
2 | --- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-13 21:03:31 +0000 |
3 | +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-15 13:25:24 +0000 |
4 | @@ -87,12 +87,14 @@ |
5 | 'pci': '0x%04x', |
6 | 'usb_device': '0x%04x', |
7 | 'scsi': '%-8s', |
8 | + 'scsi_device': '%-8s', |
9 | } |
10 | |
11 | DB_FORMAT_FOR_PRODUCT_ID = { |
12 | 'pci': '0x%04x', |
13 | 'usb_device': '0x%04x', |
14 | 'scsi': '%-16s', |
15 | + 'scsi_device': '%-16s', |
16 | } |
17 | |
18 | UDEV_USB_DEVICE_PROPERTIES = set(('DEVTYPE', 'PRODUCT', 'TYPE')) |
19 | @@ -1609,6 +1611,11 @@ |
20 | """The name of the driver contolling this device. May be None.""" |
21 | raise NotImplementedError |
22 | |
23 | + @property |
24 | + def scsi_controller(self): |
25 | + """Return the SCSI host controller for this device.""" |
26 | + raise NotImplementedError |
27 | + |
28 | def translateScsiBus(self): |
29 | """Return the real bus of a device where raw_bus=='scsi'. |
30 | |
31 | @@ -1617,37 +1624,27 @@ |
32 | for more details. This method determines the real bus |
33 | of a device accessed via the kernel's SCSI subsystem. |
34 | """ |
35 | - # While SCSI devices from valid submissions should have a |
36 | - # parent and a grandparent, we can't be sure for bogus or |
37 | - # broken submissions. |
38 | - parent = self.parent |
39 | - if parent is None: |
40 | - self.parser._logWarning( |
41 | - 'Found SCSI device without a parent: %s.' % self.device_id) |
42 | - return None |
43 | - grandparent = parent.parent |
44 | - if grandparent is None: |
45 | - self.parser._logWarning( |
46 | - 'Found SCSI device without a grandparent: %s.' |
47 | - % self.device_id) |
48 | + scsi_controller = self.scsi_controller |
49 | + if scsi_controller is None: |
50 | return None |
51 | |
52 | - grandparent_bus = grandparent.raw_bus |
53 | - if grandparent_bus == 'pci': |
54 | - if (grandparent.pci_class != PCI_CLASS_STORAGE): |
55 | + scsi_controller_bus = scsi_controller.raw_bus |
56 | + if scsi_controller_bus == 'pci': |
57 | + if (scsi_controller.pci_class != PCI_CLASS_STORAGE): |
58 | # This is not a storage class PCI device? This |
59 | # indicates a bug somewhere in HAL or in the hwdb |
60 | # client, or a fake submission. |
61 | - device_class = grandparent.pci_class |
62 | + device_class = scsi_controller.pci_class |
63 | self.parser._logWarning( |
64 | 'A (possibly fake) SCSI device %s is connected to ' |
65 | 'PCI device %s that has the PCI device class %s; ' |
66 | 'expected class 1 (storage).' |
67 | - % (self.device_id, grandparent.device_id, device_class)) |
68 | + % (self.device_id, scsi_controller.device_id, |
69 | + device_class)) |
70 | return None |
71 | - pci_subclass = grandparent.pci_subclass |
72 | + pci_subclass = scsi_controller.pci_subclass |
73 | return self.pci_storage_subclass_hwbus.get(pci_subclass) |
74 | - elif grandparent_bus == 'usb': |
75 | + elif scsi_controller_bus == 'usb': |
76 | # USB storage devices have the following HAL device hierarchy: |
77 | # - HAL node for the USB device. info.bus == 'usb_device', |
78 | # device class == 0, device subclass == 0 |
79 | @@ -2357,6 +2354,27 @@ |
80 | """See `BaseDevice`.""" |
81 | return self.getVendorOrProductID('product') |
82 | |
83 | + @property |
84 | + def scsi_controller(self): |
85 | + """See `BaseDevice`.""" |
86 | + # While SCSI devices from valid submissions should have a |
87 | + # parent and a grandparent, we can't be sure for bogus or |
88 | + # broken submissions. |
89 | + if self.raw_bus != 'scsi': |
90 | + return None |
91 | + parent = self.parent |
92 | + if parent is None: |
93 | + self.parser._logWarning( |
94 | + 'Found SCSI device without a parent: %s.' % self.device_id) |
95 | + return None |
96 | + grandparent = parent.parent |
97 | + if grandparent is None: |
98 | + self.parser._logWarning( |
99 | + 'Found SCSI device without a grandparent: %s.' |
100 | + % self.device_id) |
101 | + return None |
102 | + return grandparent |
103 | + |
104 | |
105 | class UdevDevice(BaseDevice): |
106 | """The representation of a udev device node.""" |
107 | @@ -2617,6 +2635,29 @@ |
108 | """See `BaseDevice`.""" |
109 | return self.getVendorOrProductID('product') |
110 | |
111 | + @property |
112 | + def driver_name(self): |
113 | + """See `BaseDevice`.""" |
114 | + return self.udev['E'].get('DRIVER') |
115 | + |
116 | + @property |
117 | + def scsi_controller(self): |
118 | + """See `BaseDevice`.""" |
119 | + if self.raw_bus != 'scsi_device': |
120 | + return None |
121 | + |
122 | + # While SCSI devices from valid submissions should have four |
123 | + # ancestors, we can't be sure for bogus or broken submissions. |
124 | + try: |
125 | + controller = self.parent.parent.parent.parent |
126 | + except AttributeError: |
127 | + controller = None |
128 | + if controller is None: |
129 | + self.parser._logWarning( |
130 | + 'Found a SCSI device without a sufficient number of ' |
131 | + 'ancestors: %s' % self.device_id) |
132 | + return None |
133 | + return controller |
134 | |
135 | class ProcessingLoop(object): |
136 | """An `ITunableLoop` for processing HWDB submissions.""" |
137 | |
138 | === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py' |
139 | --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-13 21:02:35 +0000 |
140 | +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-15 13:25:24 +0000 |
141 | @@ -539,6 +539,214 @@ |
142 | 'HAL property info.bus.') |
143 | |
144 | |
145 | + def test_HALDevice_scsi_controller_usb_storage_device(self): |
146 | + """test of HALDevice.scsi_controller. |
147 | + |
148 | + The physical device is a USB storage device. |
149 | + """ |
150 | + devices = [ |
151 | + # The main node of the USB storage device. |
152 | + { |
153 | + 'id': 1, |
154 | + 'udi': self.UDI_USB_STORAGE, |
155 | + 'properties': { |
156 | + 'info.bus': ('usb_device', 'str'), |
157 | + }, |
158 | + }, |
159 | + # The storage interface of the USB device. |
160 | + { |
161 | + 'id': 2, |
162 | + 'udi': self.UDI_USB_STORAGE_IF0, |
163 | + 'properties': { |
164 | + 'info.bus': ('usb', 'str'), |
165 | + 'info.parent': (self.UDI_USB_STORAGE, 'str'), |
166 | + }, |
167 | + }, |
168 | + # The fake SCSI host of the storage device. Note that HAL does |
169 | + # _not_ provide the info.bus property. |
170 | + { |
171 | + 'id': 3, |
172 | + 'udi': self.UDI_USB_STORAGE_SCSI_HOST, |
173 | + 'properties': { |
174 | + 'info.parent': (self.UDI_USB_STORAGE_IF0, 'str'), |
175 | + }, |
176 | + }, |
177 | + # The fake SCSI disk. |
178 | + { |
179 | + 'id': 3, |
180 | + 'udi': self.UDI_USB_STORAGE_SCSI_DEVICE, |
181 | + 'properties': { |
182 | + 'info.bus': ('scsi', 'str'), |
183 | + 'info.parent': (self.UDI_USB_STORAGE_SCSI_HOST, 'str'), |
184 | + }, |
185 | + }, |
186 | + ] |
187 | + parsed_data = { |
188 | + 'hardware': { |
189 | + 'hal': { |
190 | + 'devices': devices, |
191 | + }, |
192 | + }, |
193 | + } |
194 | + |
195 | + parser = SubmissionParser() |
196 | + parser.buildDeviceList(parsed_data) |
197 | + |
198 | + usb_fake_scsi_disk = parser.hal_devices[ |
199 | + self.UDI_USB_STORAGE_SCSI_DEVICE] |
200 | + usb_main_device = parser.hal_devices[self.UDI_USB_STORAGE_IF0] |
201 | + self.assertEqual(usb_main_device, usb_fake_scsi_disk.scsi_controller) |
202 | + |
203 | + def test_HALDevice_scsi_controller_pci_controller(self): |
204 | + """test of HALDevice.scsi_controller. |
205 | + |
206 | + Variant for a SCSI device connected to a PCI controller. |
207 | + """ |
208 | + devices = [ |
209 | + # The PCI host controller. |
210 | + { |
211 | + 'id': 1, |
212 | + 'udi': self.UDI_SATA_CONTROLLER, |
213 | + 'properties': { |
214 | + 'info.bus': ('pci', 'str'), |
215 | + 'pci.device_class': (PCI_CLASS_STORAGE, 'int'), |
216 | + 'pci.device_subclass': (PCI_SUBCLASS_STORAGE_SATA, |
217 | + 'int'), |
218 | + }, |
219 | + }, |
220 | + # The (fake or real) SCSI host of the storage device. |
221 | + { |
222 | + 'id': 2, |
223 | + 'udi': self.UDI_SATA_CONTROLLER_SCSI, |
224 | + 'properties': { |
225 | + 'info.parent': (self.UDI_SATA_CONTROLLER, 'str'), |
226 | + }, |
227 | + }, |
228 | + # The (possibly fake) SCSI disk. |
229 | + { |
230 | + 'id': 3, |
231 | + 'udi': self.UDI_SATA_DISK, |
232 | + 'properties': { |
233 | + 'info.bus': ('scsi', 'str'), |
234 | + 'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'), |
235 | + }, |
236 | + }, |
237 | + ] |
238 | + parsed_data = { |
239 | + 'hardware': { |
240 | + 'hal': { |
241 | + 'devices': devices, |
242 | + }, |
243 | + }, |
244 | + } |
245 | + |
246 | + parser = SubmissionParser() |
247 | + parser.buildDeviceList(parsed_data) |
248 | + |
249 | + scsi_device = parser.hal_devices[self.UDI_SATA_DISK] |
250 | + controller = parser.hal_devices[self.UDI_SATA_CONTROLLER] |
251 | + self.assertEqual(controller, scsi_device.scsi_controller) |
252 | + |
253 | + def test_HALDevice_scsi_controller_non_scsi_device(self): |
254 | + """test of HALDevice.scsi_controller. |
255 | + |
256 | + Variant for non-SCSI devices. |
257 | + """ |
258 | + devices = [ |
259 | + { |
260 | + 'id': 1, |
261 | + 'udi': self.UDI_COMPUTER, |
262 | + 'properties': {}, |
263 | + }, |
264 | + ] |
265 | + parsed_data = { |
266 | + 'hardware': { |
267 | + 'hal': { |
268 | + 'devices': devices, |
269 | + }, |
270 | + }, |
271 | + } |
272 | + |
273 | + parser = SubmissionParser() |
274 | + parser.buildDeviceList(parsed_data) |
275 | + |
276 | + device = parser.hal_devices[self.UDI_COMPUTER] |
277 | + self.assertEqual(None, device.scsi_controller) |
278 | + |
279 | + def test_HALDevice_scsi_controller_no_grandparent(self): |
280 | + """test of HALDevice.scsi_controller. |
281 | + |
282 | + Variant for a SCSI device without a grandparent device. |
283 | + """ |
284 | + devices = [ |
285 | + # The (fake or real) SCSI host of the storage device. |
286 | + { |
287 | + 'id': 1, |
288 | + 'udi': self.UDI_SATA_CONTROLLER_SCSI, |
289 | + 'properties': {}, |
290 | + }, |
291 | + # The (possibly fake) SCSI disk. |
292 | + { |
293 | + 'id': 2, |
294 | + 'udi': self.UDI_SATA_DISK, |
295 | + 'properties': { |
296 | + 'info.bus': ('scsi', 'str'), |
297 | + 'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'), |
298 | + }, |
299 | + }, |
300 | + ] |
301 | + parsed_data = { |
302 | + 'hardware': { |
303 | + 'hal': { |
304 | + 'devices': devices, |
305 | + }, |
306 | + }, |
307 | + } |
308 | + |
309 | + parser = SubmissionParser(self.log) |
310 | + parser.submission_key = 'SCSI device without grandparent device' |
311 | + parser.buildDeviceList(parsed_data) |
312 | + |
313 | + scsi_device = parser.hal_devices[self.UDI_SATA_DISK] |
314 | + self.assertEqual(None, scsi_device.scsi_controller) |
315 | + self.assertWarningMessage( |
316 | + parser.submission_key, |
317 | + "Found SCSI device without a grandparent: %s." |
318 | + % self.UDI_SATA_DISK) |
319 | + |
320 | + def test_HALDevice_scsi_controller_no_parent(self): |
321 | + """test of HALDevice.scsi_controller. |
322 | + |
323 | + Variant for a SCSI device without a parent device. |
324 | + """ |
325 | + devices = [ |
326 | + # The (possibly fake) SCSI disk. |
327 | + { |
328 | + 'id': 1, |
329 | + 'udi': self.UDI_SATA_DISK, |
330 | + 'properties': { |
331 | + 'info.bus': ('scsi', 'str'), |
332 | + }, |
333 | + }, |
334 | + ] |
335 | + parsed_data = { |
336 | + 'hardware': { |
337 | + 'hal': { |
338 | + 'devices': devices, |
339 | + }, |
340 | + }, |
341 | + } |
342 | + |
343 | + parser = SubmissionParser(self.log) |
344 | + parser.submission_key = 'SCSI device without parent device' |
345 | + parser.buildDeviceList(parsed_data) |
346 | + |
347 | + scsi_device = parser.hal_devices[self.UDI_SATA_DISK] |
348 | + self.assertEqual(None, scsi_device.scsi_controller) |
349 | + self.assertWarningMessage( |
350 | + parser.submission_key, |
351 | + "Found SCSI device without a parent: %s." % self.UDI_SATA_DISK) |
352 | + |
353 | def testHALDeviceGetRealBus(self): |
354 | """Test of HALDevice.real_bus, generic case. |
355 | |
356 | @@ -2585,65 +2793,241 @@ |
357 | 'property not treated as a real device.') |
358 | |
359 | |
360 | -class TestUdevDevice(TestCase): |
361 | +class TestUdevDevice(TestCaseHWDB): |
362 | """Tests of class UdevDevice.""" |
363 | |
364 | - layer = BaseLayer |
365 | - |
366 | - root_device = { |
367 | - 'P': '/devices/LNXSYSTM:00', |
368 | - 'E': { |
369 | - 'UDEV_LOG': '3', |
370 | - 'DEVPATH': '/devices/LNXSYSTM:00', |
371 | - 'MODALIAS': 'acpi:LNXSYSTM:', |
372 | - 'SUBSYSTEM': 'acpi', |
373 | - } |
374 | - } |
375 | - |
376 | - root_device_dmi_data = { |
377 | - '/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS', |
378 | - '/sys/class/dmi/id/product_name': 'LIFEBOOK E8210', |
379 | - } |
380 | - |
381 | - pci_device_data = { |
382 | - 'P': '/devices/pci0000:00/0000:00:1f.2', |
383 | - 'E': { |
384 | - 'PCI_CLASS': '10602', |
385 | - 'PCI_ID': '8086:27C5', |
386 | - 'PCI_SUBSYS_ID': '10CF:1387', |
387 | - 'PCI_SLOT_NAME': '0000:00:1f.2', |
388 | - 'SUBSYSTEM': 'pci', |
389 | - } |
390 | - } |
391 | - |
392 | - usb_device_data = { |
393 | - 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2', |
394 | - 'E': { |
395 | - 'SUBSYSTEM': 'usb', |
396 | - 'DEVTYPE': 'usb_device', |
397 | - 'PRODUCT': '46d/a01/1013', |
398 | - 'TYPE': '0/0/0', |
399 | - }, |
400 | - } |
401 | - |
402 | - scsi_device_data = { |
403 | - 'P': '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0', |
404 | - 'E': { |
405 | - 'SUBSYSTEM': 'scsi', |
406 | - 'DEVTYPE': 'scsi_device', |
407 | - }, |
408 | - } |
409 | - |
410 | - scsi_device_sysfs_data = { |
411 | - 'vendor': 'MATSHITA', |
412 | - 'model': 'DVD-RAM UJ-841S', |
413 | - 'type': '5', |
414 | - } |
415 | - |
416 | - no_subsystem_device_data = { |
417 | - 'P': '/devices/pnp0/00:00', |
418 | - 'E': {} |
419 | - } |
420 | + def setUp(self): |
421 | + """Setup the test environment.""" |
422 | + super(TestUdevDevice, self).setUp() |
423 | + self.root_device = { |
424 | + 'P': '/devices/LNXSYSTM:00', |
425 | + 'E': { |
426 | + 'UDEV_LOG': '3', |
427 | + 'DEVPATH': '/devices/LNXSYSTM:00', |
428 | + 'MODALIAS': 'acpi:LNXSYSTM:', |
429 | + 'SUBSYSTEM': 'acpi', |
430 | + } |
431 | + } |
432 | + |
433 | + self.root_device_dmi_data = { |
434 | + '/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS', |
435 | + '/sys/class/dmi/id/product_name': 'LIFEBOOK E8210', |
436 | + } |
437 | + |
438 | + self.pci_device_data = { |
439 | + 'P': '/devices/pci0000:00/0000:00:1f.2', |
440 | + 'E': { |
441 | + 'PCI_CLASS': '10602', |
442 | + 'PCI_ID': '8086:27C5', |
443 | + 'PCI_SUBSYS_ID': '10CF:1387', |
444 | + 'PCI_SLOT_NAME': '0000:00:1f.2', |
445 | + 'SUBSYSTEM': 'pci', |
446 | + 'DRIVER': 'ahci', |
447 | + } |
448 | + } |
449 | + |
450 | + self.usb_device_data = { |
451 | + 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2', |
452 | + 'E': { |
453 | + 'SUBSYSTEM': 'usb', |
454 | + 'DEVTYPE': 'usb_device', |
455 | + 'PRODUCT': '46d/a01/1013', |
456 | + 'TYPE': '0/0/0', |
457 | + 'DRIVER': 'usb', |
458 | + }, |
459 | + } |
460 | + |
461 | + self.pci_scsi_controller_data = { |
462 | + 'P': '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0', |
463 | + 'E': { |
464 | + 'DRIVER': 'aic7xxx', |
465 | + 'PCI_CLASS': '10000', |
466 | + 'PCI_ID': '9004:6075', |
467 | + 'PCI_SUBSYS_ID': '9004:7560', |
468 | + 'SUBSYSTEM': 'pci', |
469 | + }, |
470 | + } |
471 | + |
472 | + self.pci_scsi_controller_scsi_side_1 = { |
473 | + 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/' |
474 | + '0000:09:00.0/host6'), |
475 | + 'E': { |
476 | + 'DEVTYPE': 'scsi_host', |
477 | + 'SUBSYSTEM': 'scsi', |
478 | + }, |
479 | + } |
480 | + |
481 | + self.pci_scsi_controller_scsi_side_2 = { |
482 | + 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/' |
483 | + '0000:09:00.0/host6/scsi_host/host6'), |
484 | + 'E': { |
485 | + 'SUBSYSTEM': 'scsi_host', |
486 | + }, |
487 | + } |
488 | + |
489 | + self.scsi_scanner_target_data = { |
490 | + 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/' |
491 | + '0000:09:00.0/host6/target6:0:1'), |
492 | + 'E': { |
493 | + 'DEVTYPE': 'scsi_target', |
494 | + 'SUBSYSTEM': 'scsi' |
495 | + }, |
496 | + } |
497 | + |
498 | + self.scsi_scanner_device_data = { |
499 | + 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/' |
500 | + '0000:09:00.0/host6/target6:0:1/6:0:1:0'), |
501 | + 'E': { |
502 | + 'DEVTYPE': 'scsi_device', |
503 | + 'SUBSYSTEM': 'scsi', |
504 | + }, |
505 | + } |
506 | + |
507 | + self.scsi_scanner_device_sysfs_data = { |
508 | + 'vendor': 'FUJITSU', |
509 | + 'model': 'fi-5120Cdj', |
510 | + 'type': '6', |
511 | + } |
512 | + |
513 | + self.scsi_device_hierarchy_data = [ |
514 | + {'udev_data': self.pci_scsi_controller_data}, |
515 | + {'udev_data': self.pci_scsi_controller_scsi_side_1}, |
516 | + {'udev_data': self.pci_scsi_controller_scsi_side_2}, |
517 | + {'udev_data': self.scsi_scanner_target_data}, |
518 | + { |
519 | + 'udev_data': self.scsi_scanner_device_data, |
520 | + 'sysfs_data': self.scsi_scanner_device_sysfs_data, |
521 | + }, |
522 | + ] |
523 | + |
524 | + self.pci_ide_controller = { |
525 | + 'P': '/devices/pci0000:00/0000:00:1f.1', |
526 | + 'E': { |
527 | + 'DRIVER': 'ata_piix', |
528 | + 'PCI_CLASS': '1018A', |
529 | + 'PCI_ID': '8086:27DF', |
530 | + 'PCI_SUBSYS_ID': '10CF:1385', |
531 | + 'SUBSYSTEM': 'pci', |
532 | + }, |
533 | + } |
534 | + |
535 | + self.pci_ide_controller_scsi_side_1 = { |
536 | + 'P': '/devices/pci0000:00/0000:00:1f.1/host4', |
537 | + 'E': { |
538 | + 'DEVTYPE': 'scsi_host', |
539 | + 'SUBSYSTEM': 'scsi', |
540 | + }, |
541 | + } |
542 | + |
543 | + self.pci_ide_controller_scsi_side_2 = { |
544 | + 'P': '/devices/pci0000:00/0000:00:1f.1/host4/scsi_host/host4', |
545 | + 'E': { |
546 | + 'SUBSYSTEM': 'scsi_host', |
547 | + }, |
548 | + } |
549 | + |
550 | + self.ide_device_target_data = { |
551 | + 'P': '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0', |
552 | + 'E': { |
553 | + 'DEVTYPE': 'scsi_target', |
554 | + 'SUBSYSTEM': 'scsi', |
555 | + }, |
556 | + } |
557 | + |
558 | + self.ide_cdrom_device_data = { |
559 | + 'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/' |
560 | + '4:0:0:0'), |
561 | + 'E': { |
562 | + 'SUBSYSTEM': 'scsi', |
563 | + 'DEVTYPE': 'scsi_device', |
564 | + 'DRIVER': 'sr', |
565 | + }, |
566 | + } |
567 | + |
568 | + self.ide_cdrom_device_sysfs_data = { |
569 | + 'vendor': 'MATSHITA', |
570 | + 'model': 'DVD-RAM UJ-841S', |
571 | + 'type': '5', |
572 | + } |
573 | + |
574 | + self.ide_device_hierarchy_data = [ |
575 | + {'udev_data': self.pci_ide_controller}, |
576 | + {'udev_data': self.pci_ide_controller_scsi_side_1}, |
577 | + {'udev_data': self.pci_ide_controller_scsi_side_2}, |
578 | + {'udev_data': self.ide_device_target_data}, |
579 | + { |
580 | + 'udev_data': self.ide_cdrom_device_data, |
581 | + 'sysfs_data': self.ide_cdrom_device_sysfs_data, |
582 | + }, |
583 | + ] |
584 | + |
585 | + self.usb_storage_usb_interface = { |
586 | + 'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0', |
587 | + 'E': { |
588 | + 'DRIVER': 'usb-storage', |
589 | + 'PRODUCT': '1307/163/100', |
590 | + 'TYPE': '0/0/0', |
591 | + 'INTERFACE': '8/6/80', |
592 | + 'SUBSYSTEM': 'usb', |
593 | + }, |
594 | + } |
595 | + |
596 | + self.usb_storage_scsi_host_1 = { |
597 | + 'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7', |
598 | + 'E': { |
599 | + 'DEVTYPE': 'scsi_host', |
600 | + 'SUBSYSTEM': 'scsi', |
601 | + }, |
602 | + } |
603 | + |
604 | + self.usb_storage_scsi_host_2 = { |
605 | + 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/' |
606 | + 'scsi_host/host7'), |
607 | + 'E': { |
608 | + 'SUBSYSTEM': 'scsi_host', |
609 | + }, |
610 | + } |
611 | + |
612 | + self.usb_storage_scsi_target = { |
613 | + 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/' |
614 | + 'target7:0:0'), |
615 | + 'E': { |
616 | + 'DEVTYPE': 'scsi_target', |
617 | + 'SUBSYSTEM': 'scsi', |
618 | + }, |
619 | + } |
620 | + |
621 | + self.usb_storage_scsi_device = { |
622 | + 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/' |
623 | + 'target7:0:0/7:0:0:0'), |
624 | + 'E': { |
625 | + 'DEVTYPE': 'scsi_device', |
626 | + 'DRIVER': 'sd', |
627 | + 'SUBSYSTEM': 'scsi', |
628 | + }, |
629 | + } |
630 | + |
631 | + self.usb_storage_scsi_device_sysfs = { |
632 | + 'vendor': 'Ut163', |
633 | + 'model': 'USB2FlashStorage', |
634 | + 'type': '0', |
635 | + } |
636 | + |
637 | + self.usb_storage_hierarchy_data = [ |
638 | + {'udev_data': self.usb_storage_usb_interface}, |
639 | + {'udev_data': self.usb_storage_scsi_host_1}, |
640 | + {'udev_data': self.usb_storage_scsi_host_2}, |
641 | + {'udev_data': self.usb_storage_scsi_target}, |
642 | + { |
643 | + 'udev_data': self.usb_storage_scsi_device, |
644 | + 'sysfs_data': self.usb_storage_scsi_device_sysfs, |
645 | + }, |
646 | + ] |
647 | + |
648 | + self.no_subsystem_device_data = { |
649 | + 'P': '/devices/pnp0/00:00', |
650 | + 'E': {} |
651 | + } |
652 | |
653 | def test_device_id(self): |
654 | """Test of UdevDevice.device_id.""" |
655 | @@ -2790,7 +3174,8 @@ |
656 | def test_is_scsi_device(self): |
657 | """Test of UdevDevice.is_scsi_device.""" |
658 | device = UdevDevice( |
659 | - None, self.scsi_device_data, self.scsi_device_sysfs_data) |
660 | + None, self.scsi_scanner_device_data, |
661 | + self.scsi_scanner_device_sysfs_data) |
662 | self.assertTrue(device.is_scsi_device) |
663 | |
664 | device = UdevDevice(None, self.root_device) |
665 | @@ -2799,16 +3184,18 @@ |
666 | def test_scsi_vendor(self): |
667 | """Test of UdevDevice.scsi_vendor.""" |
668 | device = UdevDevice( |
669 | - None, self.scsi_device_data, self.scsi_device_sysfs_data, None) |
670 | - self.assertEqual('MATSHITA', device.scsi_vendor) |
671 | + None, self.scsi_scanner_device_data, |
672 | + self.scsi_scanner_device_sysfs_data) |
673 | + self.assertEqual('FUJITSU', device.scsi_vendor) |
674 | device = UdevDevice(None, self.root_device) |
675 | self.assertEqual(None, device.scsi_vendor) |
676 | |
677 | def test_scsi_model(self): |
678 | """Test of UdevDevice.scsi_model.""" |
679 | device = UdevDevice( |
680 | - None, self.scsi_device_data, self.scsi_device_sysfs_data) |
681 | - self.assertEqual('DVD-RAM UJ-841S', device.scsi_model) |
682 | + None, self.scsi_scanner_device_data, |
683 | + self.scsi_scanner_device_sysfs_data) |
684 | + self.assertEqual('fi-5120Cdj', device.scsi_model) |
685 | |
686 | device = UdevDevice(None, self.root_device) |
687 | self.assertEqual(None, device.scsi_model) |
688 | @@ -2847,10 +3234,10 @@ |
689 | self.assertEqual('Unknown', device.getVendorOrProduct('product')) |
690 | |
691 | device = UdevDevice( |
692 | - None, self.scsi_device_data, self.scsi_device_sysfs_data) |
693 | - self.assertEqual('MATSHITA', device.getVendorOrProduct('vendor')) |
694 | - self.assertEqual( |
695 | - 'DVD-RAM UJ-841S', device.getVendorOrProduct('product')) |
696 | + None, self.scsi_scanner_device_data, |
697 | + self.scsi_scanner_device_sysfs_data) |
698 | + self.assertEqual('FUJITSU', device.getVendorOrProduct('vendor')) |
699 | + self.assertEqual('fi-5120Cdj', device.getVendorOrProduct('product')) |
700 | |
701 | device = UdevDevice(None, self.no_subsystem_device_data) |
702 | self.assertEqual(None, device.getVendorOrProduct('vendor')) |
703 | @@ -2888,10 +3275,10 @@ |
704 | self.assertEqual(0xa01, device.getVendorOrProductID('product')) |
705 | |
706 | device = UdevDevice( |
707 | - None, self.scsi_device_data, self.scsi_device_sysfs_data) |
708 | - self.assertEqual('MATSHITA', device.getVendorOrProductID('vendor')) |
709 | - self.assertEqual( |
710 | - 'DVD-RAM UJ-841S', device.getVendorOrProductID('product')) |
711 | + None, self.scsi_scanner_device_data, |
712 | + self.scsi_scanner_device_sysfs_data) |
713 | + self.assertEqual('FUJITSU', device.getVendorOrProductID('vendor')) |
714 | + self.assertEqual('fi-5120Cdj', device.getVendorOrProductID('product')) |
715 | |
716 | device = UdevDevice( |
717 | None, self.no_subsystem_device_data) |
718 | @@ -2910,6 +3297,131 @@ |
719 | None, self.root_device, None, self.root_device_dmi_data) |
720 | self.assertEqual('LIFEBOOK E8210', device.product_id) |
721 | |
722 | + def test_vendor_id_for_db(self): |
723 | + """Test of UdevDevice.vendor_id_for_db.""" |
724 | + device = UdevDevice( |
725 | + None, self.root_device, None, self.root_device_dmi_data) |
726 | + self.assertEqual('FUJITSU SIEMENS', device.vendor_id_for_db) |
727 | + |
728 | + device = UdevDevice(None, self.pci_device_data) |
729 | + self.assertEqual('0x8086', device.vendor_id_for_db) |
730 | + |
731 | + device = UdevDevice(None, self.usb_device_data) |
732 | + self.assertEqual('0x046d', device.vendor_id_for_db) |
733 | + |
734 | + device = UdevDevice( |
735 | + None, self.scsi_scanner_device_data, |
736 | + self.scsi_scanner_device_sysfs_data) |
737 | + self.assertEqual('FUJITSU ', device.vendor_id_for_db) |
738 | + |
739 | + def test_product_id_for_db(self): |
740 | + """Test of UdevDevice.product_id_for_db.""" |
741 | + device = UdevDevice( |
742 | + None, self.root_device, None, self.root_device_dmi_data) |
743 | + self.assertEqual('LIFEBOOK E8210', device.product_id_for_db) |
744 | + |
745 | + device = UdevDevice(None, self.pci_device_data) |
746 | + self.assertEqual('0x27c5', device.product_id_for_db) |
747 | + |
748 | + device = UdevDevice(None, self.usb_device_data) |
749 | + self.assertEqual('0x0a01', device.product_id_for_db) |
750 | + |
751 | + device = UdevDevice( |
752 | + None, self.scsi_scanner_device_data, |
753 | + self.scsi_scanner_device_sysfs_data) |
754 | + self.assertEqual('fi-5120Cdj ', device.product_id_for_db) |
755 | + |
756 | + def test_driver_name(self): |
757 | + """Test of UdevDevice.driver_name.""" |
758 | + device = UdevDevice(None, self.pci_device_data) |
759 | + self.assertEqual('ahci', device.driver_name) |
760 | + |
761 | + device = UdevDevice( |
762 | + None, self.root_device, None, self.root_device_dmi_data) |
763 | + self.assertEqual(None, device.driver_name) |
764 | + |
765 | + def buildUdevDeviceHierarchy(self, device_data, parser=None): |
766 | + """Build a UdevDevice hierarchy from device_data. |
767 | + |
768 | + :param device_data: A sequence of arguments that are passed |
769 | + to the UdevDevice constructor. Each element must be a |
770 | + dictionary that can be used as a **kwargs argument. |
771 | + |
772 | + Element N of the sequence is the parent of element N+1. |
773 | + :param parser: A SubmissionParser instance to be passed to |
774 | + the constructor of UdevDevice. |
775 | + """ |
776 | + parent = None |
777 | + devices = [] |
778 | + for kwargs in device_data: |
779 | + device = UdevDevice(parser, **kwargs) |
780 | + devices.append(device) |
781 | + if parent is not None: |
782 | + parent.addChild(device) |
783 | + parent = device |
784 | + return devices |
785 | + |
786 | + def test_scsi_controller(self): |
787 | + """Test of UdevDevice.scsi_controller for a PCI controller.""" |
788 | + devices = self.buildUdevDeviceHierarchy( |
789 | + self.scsi_device_hierarchy_data) |
790 | + controller = devices[0] |
791 | + scsi_device = devices[-1] |
792 | + self.assertEqual(controller, scsi_device.scsi_controller) |
793 | + |
794 | + def test_scsi_controller_insufficient_anchestors(self): |
795 | + """Test of UdevDevice.scsi_controller for a PCI controller. |
796 | + |
797 | + If a SCSI device does not have a sufficient number of ancestors, |
798 | + UdevDevice.scsi_controller returns None. |
799 | + """ |
800 | + parser = SubmissionParser(self.log) |
801 | + parser.submission_key = 'UdevDevice.scsi_controller ancestor missing' |
802 | + devices = self.buildUdevDeviceHierarchy( |
803 | + self.scsi_device_hierarchy_data[1:], parser) |
804 | + scsi_device = devices[-1] |
805 | + self.assertEqual(None, scsi_device.scsi_controller) |
806 | + self.assertWarningMessage( |
807 | + parser.submission_key, |
808 | + 'Found a SCSI device without a sufficient number of ancestors: ' |
809 | + '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0/' |
810 | + 'host6/target6:0:1/6:0:1:0') |
811 | + |
812 | + def test_scsi_controller_no_scsi_device(self): |
813 | + """Test of UdevDevice.scsi_controller for a PCI controller. |
814 | + |
815 | + For non-SCSI devices, this property is None. |
816 | + """ |
817 | + device = UdevDevice(None, self.pci_device_data) |
818 | + self.assertEqual(None, device.scsi_controller) |
819 | + |
820 | + def test_translateScsiBus_real_scsi_device(self): |
821 | + """Test of UdevDevice.translateScsiBus() with a real SCSI device.""" |
822 | + devices = self.buildUdevDeviceHierarchy( |
823 | + self.scsi_device_hierarchy_data) |
824 | + scsi_device = devices[-1] |
825 | + self.assertEqual( |
826 | + HWBus.SCSI, scsi_device.translateScsiBus()) |
827 | + |
828 | + def test_translateScsiBus_ide_device(self): |
829 | + """Test of UdevDevice.translateScsiBus() with an IDE device.""" |
830 | + devices = self.buildUdevDeviceHierarchy( |
831 | + self.ide_device_hierarchy_data) |
832 | + ide_device = devices[-1] |
833 | + self.assertEqual(HWBus.IDE, ide_device.translateScsiBus()) |
834 | + |
835 | + def test_translateScsiBus_usb_device(self): |
836 | + """Test of UdevDevice.translateScsiBus() with a USB device.""" |
837 | + devices = self.buildUdevDeviceHierarchy( |
838 | + self.usb_storage_hierarchy_data) |
839 | + usb_scsi_device = devices[-1] |
840 | + self.assertEqual(None, usb_scsi_device.translateScsiBus()) |
841 | + |
842 | + def test_translateScsiBus_non_scsi_device(self): |
843 | + """Test of UdevDevice.translateScsiBus() for a non-SCSI device.""" |
844 | + device = UdevDevice(None, self.root_device) |
845 | + self.assertEqual(None, device.translateScsiBus()) |
846 | + |
847 | |
848 | class TestHWDBSubmissionTablePopulation(TestCaseHWDB): |
849 | """Tests of the HWDB popoluation with submitted data.""" |
This branch adds a property scsi_controller to class UdevDevice in the script hwdbsubmissons.py.
Aside from tests of this property, I also added tests for UdevDevice. translateScsiBu s(). While this method is defined in class BaseDevice, from which UdevDevice is derived, is accesses the property scsi_controller, so some tests seemed reasonable. (class UdevDevice represents a device mentioned in a submission made by Karmic's HWDB client, while submissions from older Ubuntu versions contain data from HAL; these devices are represnted by class HALDevice.)
Handling of SCSI devices in HWDB submissions is a bit convoluted: Many non-SCSI storage devices, like IDE/SATA disks and CD drives and USB storage devices, support the SCSI command set, and the Linux kernel treats them like real SCSI devices. The data stored about a device includes the bus used to connect the device to a computer, so we want to know the real, physical bus of a device, instead of treating nearly all storage devices as SCSI devices.
We can detect the real bus a of (real or fake) SCSI device by inspecting the type of the (real or fake) SCSI controller. This is done by BaseDevice. translateScsiBu s(). The core logic of this method is the same for submissions containing HAL and udev data, so it is located in class BaseDevice. The difference between HAL and udev data is that the controller of a SCSI device in HAL is the grandparent of the SCSI device, while it is the grand-grand- grand-parent in udev, hence the property scsi_controller is differently implemented in HALDevice and UdevDevice.
In order to setup a test of UdevDevice. scsi_controller or UdevDevice. translateScsiBu s, we need to create five UdevDevices, so I added a method buildUdevDevice Hierarchy( ) to class TestUdevDevice.
I also changed the base class of TestUdevDevice from TestCase to testCaseHWDB: The latter defines some infrastructure to assert that expected warnings or error messages really appear.
test: ./bin/test --test= test_hwdb_ submission_ processing
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: /launchpad/ scripts/ hwdbsubmissions .py /launchpad/ scripts/ tests/test_ hwdb_submission _processing. py
lib/canonical
lib/canonical
== Pyflakes notices ==
lib/canonical/ launchpad/ scripts/ hwdbsubmissions .py
22: redefinition of unused 'etree' from line 20
== Pylint notices ==
lib/canonical/ launchpad/ scripts/ hwdbsubmissions .py cElementTree' (No module named etree)
20: [F0401] Unable to import 'xml.etree.
This complaint is not related to my changes.