Merge lp:~adeuring/launchpad/hwdb-class-udev-device-9 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Merged at revision: not available
Proposed branch: lp:~adeuring/launchpad/hwdb-class-udev-device-9
Merge into: lp:launchpad
Diff against target: 829 lines
2 files modified
lib/canonical/launchpad/scripts/hwdbsubmissions.py (+43/-7)
lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py (+391/-52)
To merge this branch: bzr merge lp:~adeuring/launchpad/hwdb-class-udev-device-9
Reviewer Review Type Date Requested Status
Eleanor Berger (community) code Approve
Review via email: mp+13562@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (4.3 KiB)

This branch "adjusts" BaseDevice.is_real_device for udev data.

The HWDB submission processing script creates instances of class UdevDevice (derived from class BaseDevice) for each udev node found in the submitted data. (run "udevadm info --export-db" to see how this data looks in real like. But note that the output differs in many small details between the udev versions in Jaunty and Karmic.) In many cases udev has more than one node for a given physical device. SCSI devices, for example, have a "main" node, one sub-node for the the "disk aspect" of the device, another sub-node for the generic SCSI driver (allowing you to control the device with "raw" SCSI commands from a user-space program) etc.

We do not want to store these fine details in the HWDB -- we want to store data about a physical device just once. The decision if a given UdevDevice node represents a physical device is made by the property BaseDevice.is_real_device.

The code of this property accessed the property HALDevice.udi which is not available for class UdevDevice; this is replaced by is_root_device.

The core idea of is_real_device is to base the decision on the value of the property raw_bus. This does not work very well for the root device, where raw_bus is None or "Unknown" for submissions with HAL data, and "acpi" for udev submissions. So I moved the case for the root device to the top of the method, to keep the remaining code a bit more clean.

There are some differences in the values of raw_bus between HAL and udev submissions: For udev data, we can have raw_bus set to "partition", "scsi_disk", "scsi_target", "spi_transport". Such nodes desribe "aspects" of SCSI devices, but not real devices, so I added these values to "if bus in (None..."

These values do not appear in submissions with HAL data, so there is no bad side effect of this change.

The raw_bus is "scsi_device" for the udev node describing the "physical" device, while it is "scsi" for submissions with HAL data, so I had to adjust "elif bus == 'scsi':" too. Again, "scsi_device" is not used in submissions with HAL data, so no bad side effect.

Finally, I had to adjust UdevDevice.raw_bus (diff @@ -2564,7 +2590,17 @@). The core idea if the property is to access the HAL property "info.bus" or "info.subsystem" (depending on the HAL version), and to access "something correspondent" for udev devices, where we use the udev properties SUBSYSTEM and DEVTYPE. The name is intended to show a "contrast" to another property real_bus, which may differ for PCI devices (where real_bus may be "PC Card") and for SCSI devices, which may in fact be IDE, ATA or USB devices.

The idea to use DEVTYPE, if this udev property is available, or SUBSYSTEM otherwsie, works mostly fine, except for a certain sub-node of a real SCSI device, where SUBSYSTEM is "scsi_device" and DEVTYPE is None. This leads to conflicts with the main node of a SCSI device, where SUBSYSTEM is "scsi", and DEVTYPE is "scsi_device". The former node should not be treated as a real device, while the latter should be... So I introduced the "if subsystem != 'scsi_device':" clause. This makes the property raw_bus, let's say, "less raw", but I could not come up with a ...

Read more...

Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (24.8 KiB)

This branch is based on lp:~adeuring/launchpad/hwdb-class-udev-device-9, which is reviewd, but has not yet landed. Here is the diff against that branch:

=== modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
--- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-18 17:44:12 +0000
+++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-19 10:41:31 +0000
@@ -1835,6 +1835,9 @@
             Since these components are not the most important ones
             for the HWDB, we'll ignore them for now. Bug 237038.

+ - 'disk' is used udev submissions for a node related to the
+ sd or sr driver of (real or fake) SCSI block devices.
+
           - info.bus == 'drm' is used by the HAL for the direct
             rendering interface of a graphics card.

@@ -1847,6 +1850,12 @@
           - info.bus == 'net' is used by the HAL version in
             Intrepid for the "output aspects" of network devices.

+ - 'partition' is used in udev submissions for a node
+ related to disk partition
+
+ - 'scsi_disk' is used in udev submissions for a sub-node of
+ the real device node.
+
             info.bus == 'scsi_generic' is used by the HAL version in
             Intrepid for a HAL node representing the generic
             interface of a SCSI device.
@@ -1858,6 +1867,12 @@
             "SCSI aspect" of another HAL node which represents the
             real device.

+ 'scsi_target' is used in udev data for SCSI target nodes,
+ the parent of a SCSI device (or LUN) node.
+
+ 'spi_transport' (SCSI Parallel Transport) is used in
+ udev data for a sub-node of real SCSI devices.
+
             info.bus == 'sound' is used by the HAL version in
             Intrepid for "aspects" of sound devices.

@@ -1873,20 +1888,31 @@
             info.bus == 'usb' is used for end points of USB devices;
             the root node of a USB device has info.bus == 'usb_device'.

+ 'usb_interface' is used in udv submissions for interface
+ nodes of USB devices.
+
             info.bus == 'video4linux' is used for the "input aspect"
             of video devices.
         """
+ # The root node is always a real device, but its raw_bus
+ # property can have different values: None or 'Unknown' in
+ # submissions with HAL data, 'acpi' for submissions with udev
+ # data.
+ if self.is_root_device:
+ return True
+
         bus = self.raw_bus
         # This set of buses is only used once; it's easier to have it
         # here than to put it elsewhere and have to document its
         # location and purpose.
- if bus in (None, 'drm', 'dvb', 'memstick_host', 'net',
- 'scsi_generic', 'scsi_host', 'sound', 'ssb', 'tty',
- 'usb', 'video4linux', ):
+ if bus in (None, 'disk', 'drm', 'dvb', 'memstick_host', 'net',
+ 'partition', 'scsi_disk', 'scsi_generic', 'scsi_host',
+ 'scsi_target', 'sound', 'spi_transport', 'ssb', 'tty',
+ 'usb', 'usb_interface', 'video4linux', ):
             #
          ...

Revision history for this message
Abel Deuring (adeuring) wrote :
Revision history for this message
Eleanor Berger (intellectronica) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
2--- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-15 13:59:33 +0000
3+++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-19 11:30:32 +0000
4@@ -1835,6 +1835,9 @@
5 Since these components are not the most important ones
6 for the HWDB, we'll ignore them for now. Bug 237038.
7
8+ - 'disk' is used udev submissions for a node related to the
9+ sd or sr driver of (real or fake) SCSI block devices.
10+
11 - info.bus == 'drm' is used by the HAL for the direct
12 rendering interface of a graphics card.
13
14@@ -1847,6 +1850,12 @@
15 - info.bus == 'net' is used by the HAL version in
16 Intrepid for the "output aspects" of network devices.
17
18+ - 'partition' is used in udev submissions for a node
19+ related to disk partition
20+
21+ - 'scsi_disk' is used in udev submissions for a sub-node of
22+ the real device node.
23+
24 info.bus == 'scsi_generic' is used by the HAL version in
25 Intrepid for a HAL node representing the generic
26 interface of a SCSI device.
27@@ -1858,6 +1867,12 @@
28 "SCSI aspect" of another HAL node which represents the
29 real device.
30
31+ 'scsi_target' is used in udev data for SCSI target nodes,
32+ the parent of a SCSI device (or LUN) node.
33+
34+ 'spi_transport' (SCSI Parallel Transport) is used in
35+ udev data for a sub-node of real SCSI devices.
36+
37 info.bus == 'sound' is used by the HAL version in
38 Intrepid for "aspects" of sound devices.
39
40@@ -1873,20 +1888,31 @@
41 info.bus == 'usb' is used for end points of USB devices;
42 the root node of a USB device has info.bus == 'usb_device'.
43
44+ 'usb_interface' is used in udv submissions for interface
45+ nodes of USB devices.
46+
47 info.bus == 'video4linux' is used for the "input aspect"
48 of video devices.
49 """
50+ # The root node is always a real device, but its raw_bus
51+ # property can have different values: None or 'Unknown' in
52+ # submissions with HAL data, 'acpi' for submissions with udev
53+ # data.
54+ if self.is_root_device:
55+ return True
56+
57 bus = self.raw_bus
58 # This set of buses is only used once; it's easier to have it
59 # here than to put it elsewhere and have to document its
60 # location and purpose.
61- if bus in (None, 'drm', 'dvb', 'memstick_host', 'net',
62- 'scsi_generic', 'scsi_host', 'sound', 'ssb', 'tty',
63- 'usb', 'video4linux', ):
64+ if bus in (None, 'disk', 'drm', 'dvb', 'memstick_host', 'net',
65+ 'partition', 'scsi_disk', 'scsi_generic', 'scsi_host',
66+ 'scsi_target', 'sound', 'spi_transport', 'ssb', 'tty',
67+ 'usb', 'usb_interface', 'video4linux', ):
68 #
69 # The computer itself is the only HAL device without the
70 # info.bus property that we treat as a real device.
71- return self.udi == ROOT_UDI
72+ return False
73 elif bus == 'usb_device':
74 vendor_id = self.usb_vendor_id
75 product_id = self.usb_product_id
76@@ -1910,7 +1936,7 @@
77 'host controller: %s' % self.udi)
78 return False
79 return True
80- elif bus == 'scsi':
81+ elif bus in ('scsi', 'scsi_device'):
82 # Ensure consistency with HALDevice.real_bus
83 return self.real_bus is not None
84 else:
85@@ -2564,7 +2590,17 @@
86 devtype = properties.get('DEVTYPE')
87 if devtype is not None:
88 return devtype
89- return properties.get('SUBSYSTEM')
90+ subsystem = properties.get('SUBSYSTEM')
91+ # A real mess: The main node of a SCSI device has
92+ # SUBSYSTEM = 'scsi' and DEVTYPE = 'scsi_device', while
93+ # a sub-node has SUBSYSTEM='scsi_device'. We don't want
94+ # the two to be confused. The latter node is not of any
95+ # interest for us, so we return None. This ensures that
96+ # is_real_device returns False for the sub-node.
97+ if subsystem != 'scsi_device':
98+ return subsystem
99+ else:
100+ return None
101
102 @property
103 def is_root_device(self):
104@@ -2665,7 +2701,7 @@
105 # While SCSI devices from valid submissions should have four
106 # ancestors, we can't be sure for bogus or broken submissions.
107 try:
108- controller = self.parent.parent.parent.parent
109+ controller = self.parent.parent.parent
110 except AttributeError:
111 controller = None
112 if controller is None:
113
114=== modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
115--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-15 15:28:38 +0000
116+++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-19 11:30:32 +0000
117@@ -2859,18 +2859,6 @@
118 '/sys/class/dmi/id/product_name': 'LIFEBOOK E8210',
119 }
120
121- self.pci_device_data = {
122- 'P': '/devices/pci0000:00/0000:00:1f.2',
123- 'E': {
124- 'PCI_CLASS': '10602',
125- 'PCI_ID': '8086:27C5',
126- 'PCI_SUBSYS_ID': '10CF:1387',
127- 'PCI_SLOT_NAME': '0000:00:1f.2',
128- 'SUBSYSTEM': 'pci',
129- 'DRIVER': 'ahci',
130- }
131- }
132-
133 self.usb_device_data = {
134 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2',
135 'E': {
136@@ -2882,8 +2870,10 @@
137 },
138 }
139
140+ self.pci_pccard_bridge_path = (
141+ '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0')
142 self.pci_pccard_bridge = {
143- 'P': '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0',
144+ 'P': self.pci_pccard_bridge_path,
145 'E': {
146 'DRIVER': 'yenta_cardbus',
147 'PCI_CLASS': '60700',
148@@ -2894,8 +2884,10 @@
149 }
150 }
151
152+ self.pccard_scsi_controller_path = (
153+ '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0')
154 self.pccard_scsi_controller_data = {
155- 'P': '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0',
156+ 'P': self.pccard_scsi_controller_path,
157 'E': {
158 'DRIVER': 'aic7xxx',
159 'PCI_CLASS': '10000',
160@@ -2937,9 +2929,11 @@
161 },
162 }
163
164+ self.scsi_scanner_device_path = (
165+ '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0/'
166+ 'host6/target6:0:1/6:0:1:0')
167 self.scsi_scanner_device_data = {
168- 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
169- '0000:09:00.0/host6/target6:0:1/6:0:1:0'),
170+ 'P': self.scsi_scanner_device_path,
171 'E': {
172 'DEVTYPE': 'scsi_device',
173 'SUBSYSTEM': 'scsi',
174@@ -2952,6 +2946,31 @@
175 'type': '6',
176 }
177
178+ self.scsi_scanner_device_data_2 = {
179+ 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
180+ '0000:09:00.0/host6/target6:0:1/6:0:1:0/scsi_device/'
181+ '6:0:1:0'),
182+ 'E': {
183+ 'SUBSYSTEM': 'scsi_device',
184+ },
185+ }
186+
187+ self.scsi_scanner_scsi_generic = {
188+ 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
189+ '0000:09:00.0/host6/target6:0:1/6:0:1:0/scsi_generic/sg2'),
190+ 'E': {
191+ 'SUBSYSTEM': 'scsi_generic',
192+ },
193+ }
194+
195+ self.scsi_scanner_spi = {
196+ 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
197+ '0000:09:00.0/host6/target6:0:1/spi_transport/target6:0:1'),
198+ 'E': {
199+ 'SUBSYSTEM': 'spi_transport',
200+ },
201+ }
202+
203 self.scsi_device_hierarchy_data = [
204 {'udev_data': self.pccard_scsi_controller_data},
205 {'udev_data': self.pci_scsi_controller_scsi_side_1},
206@@ -2961,10 +2980,14 @@
207 'udev_data': self.scsi_scanner_device_data,
208 'sysfs_data': self.scsi_scanner_device_sysfs_data,
209 },
210+ {'udev_data': self.scsi_scanner_device_data_2},
211+ {'udev_data': self.scsi_scanner_scsi_generic},
212+ {'udev_data': self.scsi_scanner_spi},
213 ]
214
215+ self.pci_ide_controller_path = '/devices/pci0000:00/0000:00:1f.1'
216 self.pci_ide_controller = {
217- 'P': '/devices/pci0000:00/0000:00:1f.1',
218+ 'P': self.pci_ide_controller_path,
219 'E': {
220 'DRIVER': 'ata_piix',
221 'PCI_CLASS': '1018A',
222@@ -2997,9 +3020,10 @@
223 },
224 }
225
226+ self.ide_cdrom_device_path = (
227+ '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0')
228 self.ide_cdrom_device_data = {
229- 'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
230- '4:0:0:0'),
231+ 'P': self.ide_cdrom_device_path,
232 'E': {
233 'SUBSYSTEM': 'scsi',
234 'DEVTYPE': 'scsi_device',
235@@ -3013,6 +3037,31 @@
236 'type': '5',
237 }
238
239+ self.ide_cdrom_sr_data = {
240+ 'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
241+ '4:0:0:0/block/sr0'),
242+ 'E': {
243+ 'DEVTYPE': 'disk',
244+ 'SUBSYSTEM': 'block',
245+ },
246+ }
247+
248+ self.ide_cdrom_device_data_2 = {
249+ 'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
250+ '4:0:0:0/scsi_device/4:0:0:0'),
251+ 'E': {
252+ 'SUBSYSTEM': 'scsi_device',
253+ },
254+ }
255+
256+ self.ide_cdrom_scsi_generic_data = {
257+ 'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
258+ '4:0:0:0/scsi_generic/sg1'),
259+ 'E': {
260+ 'SUBSYSTEM': 'scsi_generic',
261+ },
262+ }
263+
264 self.ide_device_hierarchy_data = [
265 {'udev_data': self.pci_ide_controller},
266 {'udev_data': self.pci_ide_controller_scsi_side_1},
267@@ -3022,8 +3071,136 @@
268 'udev_data': self.ide_cdrom_device_data,
269 'sysfs_data': self.ide_cdrom_device_sysfs_data,
270 },
271+ {'udev_data': self.ide_cdrom_sr_data},
272+ {'udev_data': self.ide_cdrom_device_data_2},
273+ {'udev_data': self.ide_cdrom_scsi_generic_data},
274 ]
275
276+ self.pci_sata_controller_path = '/devices/pci0000:00/0000:00:1f.2'
277+ self.pci_sata_controller = {
278+ 'P': self.pci_sata_controller_path,
279+ 'E': {
280+ 'PCI_CLASS': '10602',
281+ 'PCI_ID': '8086:27C5',
282+ 'PCI_SUBSYS_ID': '10CF:1387',
283+ 'PCI_SLOT_NAME': '0000:00:1f.2',
284+ 'SUBSYSTEM': 'pci',
285+ 'DRIVER': 'ahci',
286+ }
287+ }
288+
289+ self.pci_sata_controller_scsi_side_1 = {
290+ 'P': '/devices/pci0000:00/0000:00:1f.2/host0',
291+ 'E': {
292+ 'DEVTYPE': 'scsi_host',
293+ 'SUBSYSTEM': 'scsi',
294+ },
295+ }
296+
297+ self.pci_sata_controller_scsi_side_2 = {
298+ 'P': '/devices/pci0000:00/0000:00:1f.2/host0/scsi_host/host0',
299+ 'E': {
300+ 'SUBSYSTEM': 'scsi_host',
301+ },
302+ }
303+
304+ self.sata_disk_target_data = {
305+ 'P': '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0',
306+ 'E': {
307+ 'DEVTYPE': 'scsi_target',
308+ 'SUBSYSTEM': 'scsi',
309+ },
310+ }
311+
312+ self.sata_disk_device_path = (
313+ '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0')
314+ self.sata_disk_device_data = {
315+ 'P': self.sata_disk_device_path,
316+ 'E': {
317+ 'DEVTYPE': 'scsi_device',
318+ 'DRIVER': 'sd',
319+ 'SUBSYSTEM': 'scsi',
320+ },
321+ }
322+
323+ self.sata_disk_device_sysfs_data = {
324+ 'vendor': 'ATA',
325+ 'model': 'Hitachi HTS54251',
326+ 'type': '0',
327+ }
328+
329+ self.sata_disk_device_data_2 = {
330+ 'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
331+ '0:0:0:0/scsi_device/0:0:0:0'),
332+ 'E': {
333+ 'SUBSYSTEM': 'scsi_device',
334+ },
335+ }
336+
337+ self.sata_disk_device_scsi_disk_data = {
338+ 'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
339+ '0:0:0:0/scsi_disk/0:0:0:0'),
340+ 'E': {
341+ 'SUBSYSTEM': 'scsi_disk',
342+ },
343+ }
344+
345+ self.sata_disk_device_scsi_generic_data = {
346+ 'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
347+ '0:0:0:0/scsi_generic/sg0'),
348+ 'E': {
349+ 'SUBSYSTEM': 'scsi_generic'
350+ },
351+ }
352+
353+ self.sata_disk_block_data = {
354+ 'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
355+ '0:0:0:0/block/sda'),
356+ 'E': {
357+ 'DEVTYPE': 'disk',
358+ 'SUBSYSTEM': 'block',
359+ },
360+ }
361+
362+ self.sata_disk_partition_data = {
363+ 'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
364+ '0:0:0:0/block/sda/sda1'),
365+ 'E': {
366+ 'DEVTYPE': 'partition',
367+ 'SUBSYSTEM': 'block',
368+ },
369+ }
370+
371+ self.sata_device_hierarchy_data = [
372+ {'udev_data': self.pci_sata_controller},
373+ {'udev_data': self.pci_sata_controller_scsi_side_1},
374+ {'udev_data': self.pci_sata_controller_scsi_side_2},
375+ {'udev_data': self.sata_disk_target_data},
376+ {'udev_data': self.sata_disk_device_data},
377+ {
378+ 'udev_data': self.sata_disk_device_data,
379+ 'sysfs_data': self.sata_disk_device_sysfs_data,
380+ },
381+ {'udev_data': self.sata_disk_device_data_2},
382+ {'udev_data': self.sata_disk_device_scsi_disk_data},
383+ {'udev_data': self.sata_disk_device_scsi_generic_data},
384+ {'udev_data': self.sata_disk_block_data},
385+ {'udev_data': self.sata_disk_partition_data},
386+ ]
387+
388+ self.usb_storage_usb_device_path = (
389+ '/devices/pci0000:00/0000:00:1d.7/usb1/1-1')
390+ self.usb_storage_usb_device_data = {
391+ 'P': self.usb_storage_usb_device_path,
392+ 'E': {
393+ 'DEVTYPE': 'usb_device',
394+ 'DRIVER': 'usb',
395+ 'PRODUCT': '1307/163/100',
396+ 'TYPE': '0/0/0',
397+ 'SUBSYSTEM': 'usb',
398+ },
399+ }
400+
401 self.usb_storage_usb_interface = {
402 'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0',
403 'E': {
404@@ -3061,9 +3238,11 @@
405 },
406 }
407
408+ self.usb_storage_scsi_device_path = (
409+ '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
410+ 'target7:0:0/7:0:0:0')
411 self.usb_storage_scsi_device = {
412- 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
413- 'target7:0:0/7:0:0:0'),
414+ 'P': self.usb_storage_scsi_device_path,
415 'E': {
416 'DEVTYPE': 'scsi_device',
417 'DRIVER': 'sd',
418@@ -3077,7 +3256,50 @@
419 'type': '0',
420 }
421
422+ self.usb_storage_scsi_device_2 = {
423+ 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
424+ 'target7:0:0/7:0:0:0/scsi_device/7:0:0:0'),
425+ 'E': {
426+ 'SUBSYSTEM': 'scsi_device',
427+ },
428+ }
429+
430+ self.usb_storage_scsi_disk = {
431+ 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
432+ 'target7:0:0/7:0:0:0/scsi_disk/7:0:0:0'),
433+ 'E': {
434+ 'SUBSYSTEM': 'scsi_disk',
435+ },
436+ }
437+
438+ self.usb_storage_scsi_generic = {
439+ 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
440+ 'target7:0:0/7:0:0:0/scsi_generic/sg3'),
441+ 'E': {
442+ 'SUBSYSTEM': 'scsi_generic',
443+ },
444+ }
445+
446+ self.usb_storage_block_device_data = {
447+ 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
448+ 'target7:0:0/7:0:0:0/block/sdb'),
449+ 'E': {
450+ 'DEVTYPE': 'disk',
451+ 'SUBSYSTEM': 'block',
452+ },
453+ }
454+
455+ self.usb_storage_block_partition_data = {
456+ 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
457+ 'target7:0:0/7:0:0:0/block/sdb/sdb1'),
458+ 'E': {
459+ 'DEVTYPE': 'partition',
460+ 'SUBSYSTEM': 'block',
461+ },
462+ }
463+
464 self.usb_storage_hierarchy_data = [
465+ {'udev_data': self.usb_storage_usb_device_data},
466 {'udev_data': self.usb_storage_usb_interface},
467 {'udev_data': self.usb_storage_scsi_host_1},
468 {'udev_data': self.usb_storage_scsi_host_2},
469@@ -3086,6 +3308,11 @@
470 'udev_data': self.usb_storage_scsi_device,
471 'sysfs_data': self.usb_storage_scsi_device_sysfs,
472 },
473+ {'udev_data': self.usb_storage_scsi_device_2},
474+ {'udev_data': self.usb_storage_scsi_disk},
475+ {'udev_data': self.usb_storage_scsi_generic},
476+ {'udev_data': self.usb_storage_block_device_data},
477+ {'udev_data': self.usb_storage_block_partition_data},
478 ]
479
480 self.no_subsystem_device_data = {
481@@ -3095,7 +3322,7 @@
482
483 def test_device_id(self):
484 """Test of UdevDevice.device_id."""
485- device = UdevDevice(None, self.pci_device_data)
486+ device = UdevDevice(None, self.pci_sata_controller)
487 self.assertEqual(
488 '/devices/pci0000:00/0000:00:1f.2', device.device_id,
489 'Unexpected value of UdevDevice.device_id.')
490@@ -3121,7 +3348,7 @@
491
492 def test_is_pci(self):
493 """Test of UdevDevice.is_pci."""
494- device = UdevDevice(None, self.pci_device_data)
495+ device = UdevDevice(None, self.pci_sata_controller)
496 self.assertTrue(device.is_pci)
497
498 device = UdevDevice(None, self.root_device)
499@@ -3129,7 +3356,7 @@
500
501 def test_pci_class_info(self):
502 """Test of UdevDevice.pci_class_info"""
503- device = UdevDevice(None, self.pci_device_data)
504+ device = UdevDevice(None, self.pci_sata_controller)
505 self.assertEqual(
506 (1, 6, 2), device.pci_class_info,
507 'Invalid value of UdevDevice.pci_class_info for PCI device.')
508@@ -3141,7 +3368,7 @@
509
510 def test_pci_class(self):
511 """Test of UdevDevice.pci_class"""
512- device = UdevDevice(None, self.pci_device_data)
513+ device = UdevDevice(None, self.pci_sata_controller)
514 self.assertEqual(
515 1, device.pci_class,
516 'Invalid value of UdevDevice.pci_class for PCI device.')
517@@ -3153,7 +3380,7 @@
518
519 def test_pci_subclass(self):
520 """Test of UdevDevice.pci_subclass"""
521- device = UdevDevice(None, self.pci_device_data)
522+ device = UdevDevice(None, self.pci_sata_controller)
523 self.assertEqual(
524 6, device.pci_subclass,
525 'Invalid value of UdevDevice.pci_class for PCI device.')
526@@ -3165,7 +3392,7 @@
527
528 def test_pci_ids(self):
529 """Test of UdevDevice.pci_ids"""
530- device = UdevDevice(None, self.pci_device_data)
531+ device = UdevDevice(None, self.pci_sata_controller)
532 self.assertEqual(
533 {'vendor': 0x8086,
534 'product': 0x27C5,
535@@ -3186,7 +3413,7 @@
536 device = UdevDevice(None, self.usb_device_data)
537 self.assertTrue(device.is_usb)
538
539- device = UdevDevice(None, self.pci_device_data)
540+ device = UdevDevice(None, self.pci_sata_controller)
541 self.assertFalse(device.is_usb)
542
543 def test_usb_ids(self):
544@@ -3269,7 +3496,7 @@
545 device = UdevDevice(None, self.root_device)
546 self.assertEqual(None, device.raw_bus)
547
548- device = UdevDevice(None, self.pci_device_data)
549+ device = UdevDevice(None, self.pci_sata_controller)
550 self.assertEqual('pci', device.raw_bus)
551
552 device = UdevDevice(None, self.usb_device_data)
553@@ -3283,7 +3510,7 @@
554 device = UdevDevice(None, self.root_device)
555 self.assertTrue(device.is_root_device)
556
557- device = UdevDevice(None, self.pci_device_data)
558+ device = UdevDevice(None, self.pci_sata_controller)
559 self.assertFalse(device.is_root_device)
560
561 def test_getVendorOrProduct(self):
562@@ -3297,7 +3524,7 @@
563 self.assertRaises(
564 AssertionError, device.getVendorOrProduct, 'nonsense')
565
566- device = UdevDevice(None, self.pci_device_data)
567+ device = UdevDevice(None, self.pci_sata_controller)
568 self.assertEqual('Unknown', device.getVendorOrProduct('vendor'))
569 self.assertEqual('Unknown', device.getVendorOrProduct('product'))
570
571@@ -3338,7 +3565,7 @@
572 self.assertRaises(
573 AssertionError, device.getVendorOrProductID, 'nonsense')
574
575- device = UdevDevice(None, self.pci_device_data)
576+ device = UdevDevice(None, self.pci_sata_controller)
577 self.assertEqual(0x8086, device.getVendorOrProductID('vendor'))
578 self.assertEqual(0x27C5, device.getVendorOrProductID('product'))
579
580@@ -3375,7 +3602,7 @@
581 None, self.root_device, None, self.root_device_dmi_data)
582 self.assertEqual('FUJITSU SIEMENS', device.vendor_id_for_db)
583
584- device = UdevDevice(None, self.pci_device_data)
585+ device = UdevDevice(None, self.pci_sata_controller)
586 self.assertEqual('0x8086', device.vendor_id_for_db)
587
588 device = UdevDevice(None, self.usb_device_data)
589@@ -3392,7 +3619,7 @@
590 None, self.root_device, None, self.root_device_dmi_data)
591 self.assertEqual('LIFEBOOK E8210', device.product_id_for_db)
592
593- device = UdevDevice(None, self.pci_device_data)
594+ device = UdevDevice(None, self.pci_sata_controller)
595 self.assertEqual('0x27c5', device.product_id_for_db)
596
597 device = UdevDevice(None, self.usb_device_data)
598@@ -3405,7 +3632,7 @@
599
600 def test_driver_name(self):
601 """Test of UdevDevice.driver_name."""
602- device = UdevDevice(None, self.pci_device_data)
603+ device = UdevDevice(None, self.pci_sata_controller)
604 self.assertEqual('ahci', device.driver_name)
605
606 device = UdevDevice(
607@@ -3423,22 +3650,42 @@
608 :param parser: A SubmissionParser instance to be passed to
609 the constructor of UdevDevice.
610 """
611- parent = None
612- devices = []
613+ devices = {}
614 for kwargs in device_data:
615 device = UdevDevice(parser, **kwargs)
616- devices.append(device)
617- if parent is not None:
618- parent.addChild(device)
619+ devices[device.device_id] = device
620 parent = device
621+
622+ # Build the parent-child relations so that the parent device
623+ # is that device which has the longest path matching the
624+ # start of the child's path.
625+ #
626+ # There is one exception of this rule: The root device has
627+ # the path "/devices/LNXSYSTM:00", but the paths of most of
628+ # our test deviies start with "/devices/pci". Well patch the
629+ # index temporarily in order to find children of the root
630+ # device.
631+ if '/devices/LNXSYSTM:00' in devices:
632+ devices['/devices'] = devices['/devices/LNXSYSTM:00']
633+ del devices['/devices/LNXSYSTM:00']
634+
635+ device_paths = sorted(devices, key=len, reverse=True)
636+ for path_index, path in enumerate(device_paths):
637+ for parent_path in device_paths[path_index+1:]:
638+ if path.startswith(parent_path):
639+ devices[parent_path].addChild(devices[path])
640+ break
641+ if '/devices' in devices:
642+ devices['/devices/LNXSYSTM:00'] = devices['/devices']
643+ del devices['/devices']
644 return devices
645
646 def test_scsi_controller(self):
647 """Test of UdevDevice.scsi_controller for a PCI controller."""
648 devices = self.buildUdevDeviceHierarchy(
649 self.scsi_device_hierarchy_data)
650- controller = devices[0]
651- scsi_device = devices[-1]
652+ controller = devices[self.pccard_scsi_controller_path]
653+ scsi_device = devices[self.scsi_scanner_device_path]
654 self.assertEqual(controller, scsi_device.scsi_controller)
655
656 def test_scsi_controller_insufficient_anchestors(self):
657@@ -3451,7 +3698,7 @@
658 parser.submission_key = 'UdevDevice.scsi_controller ancestor missing'
659 devices = self.buildUdevDeviceHierarchy(
660 self.scsi_device_hierarchy_data[1:], parser)
661- scsi_device = devices[-1]
662+ scsi_device = devices[self.scsi_scanner_device_path]
663 self.assertEqual(None, scsi_device.scsi_controller)
664 self.assertWarningMessage(
665 parser.submission_key,
666@@ -3464,14 +3711,14 @@
667
668 For non-SCSI devices, this property is None.
669 """
670- device = UdevDevice(None, self.pci_device_data)
671+ device = UdevDevice(None, self.pci_sata_controller)
672 self.assertEqual(None, device.scsi_controller)
673
674 def test_translateScsiBus_real_scsi_device(self):
675 """Test of UdevDevice.translateScsiBus() with a real SCSI device."""
676 devices = self.buildUdevDeviceHierarchy(
677 self.scsi_device_hierarchy_data)
678- scsi_device = devices[-1]
679+ scsi_device = devices[self.scsi_scanner_device_path]
680 self.assertEqual(
681 HWBus.SCSI, scsi_device.translateScsiBus())
682
683@@ -3479,14 +3726,14 @@
684 """Test of UdevDevice.translateScsiBus() with an IDE device."""
685 devices = self.buildUdevDeviceHierarchy(
686 self.ide_device_hierarchy_data)
687- ide_device = devices[-1]
688+ ide_device = devices[self.ide_cdrom_device_path]
689 self.assertEqual(HWBus.IDE, ide_device.translateScsiBus())
690
691 def test_translateScsiBus_usb_device(self):
692 """Test of UdevDevice.translateScsiBus() with a USB device."""
693 devices = self.buildUdevDeviceHierarchy(
694 self.usb_storage_hierarchy_data)
695- usb_scsi_device = devices[-1]
696+ usb_scsi_device = devices[self.usb_storage_scsi_device_path]
697 self.assertEqual(None, usb_scsi_device.translateScsiBus())
698
699 def test_translateScsiBus_non_scsi_device(self):
700@@ -3498,8 +3745,8 @@
701 """Test of UdevDevice.translatePciBus()."""
702 devices = self.buildUdevDeviceHierarchy(
703 self.pci_bridge_pccard_hierarchy_data)
704- pci_device = devices[1]
705- pccard_device = devices[2]
706+ pci_device = devices[self.pci_pccard_bridge_path]
707+ pccard_device = devices[self.pccard_scsi_controller_path]
708 self.assertEqual(HWBus.PCI, pci_device.translatePciBus())
709 self.assertEqual(HWBus.PCCARD, pccard_device.translatePciBus())
710
711@@ -3525,8 +3772,8 @@
712 """Test of UdevDevice.real_bus for PCI devices."""
713 devices = self.buildUdevDeviceHierarchy(
714 self.pci_bridge_pccard_hierarchy_data)
715- pci_device = devices[1]
716- pccard_device = devices[2]
717+ pci_device = devices[self.pci_pccard_bridge_path]
718+ pccard_device = devices[self.pccard_scsi_controller_path]
719 self.assertEqual(HWBus.PCI, pci_device.real_bus)
720 self.assertEqual(HWBus.PCCARD, pccard_device.real_bus)
721
722@@ -3534,7 +3781,7 @@
723 """Test of UdevDevice.real_bus for a SCSI device."""
724 devices = self.buildUdevDeviceHierarchy(
725 self.scsi_device_hierarchy_data)
726- scsi_device = devices[-1]
727+ scsi_device = devices[self.scsi_scanner_device_path]
728 self.assertEqual(HWBus.SCSI, scsi_device.real_bus)
729
730 def test_real_bus_system(self):
731@@ -3542,6 +3789,98 @@
732 root_device = UdevDevice(None, self.root_device)
733 self.assertEqual(HWBus.SYSTEM, root_device.real_bus)
734
735+ def test_is_real_device_root_device(self):
736+ """Test of UdevDevice._is_real_device for the root device."""
737+ root_device = UdevDevice(None, self.root_device)
738+ self.assertTrue(root_device.is_real_device)
739+
740+ def test_is_real_device_pci_device(self):
741+ """Test of UdevDevice._is_real_device for a PCI device."""
742+ pci_device = UdevDevice(None, self.pci_sata_controller)
743+ self.assertTrue(pci_device.is_real_device)
744+
745+ def test_is_real_device_scsi_device_related_nodes(self):
746+ """Test of UdevDevice._is_real_device for SCSI related nodes.
747+
748+ A SCSI device and its controller are represented by several
749+ nodes which describe different aspects. Only the controller
750+ itself and the node representing the SCSI device are
751+ considered to be real devices.
752+ """
753+ devices = self.buildUdevDeviceHierarchy(
754+ self.scsi_device_hierarchy_data)
755+ real_devices = (
756+ self.pccard_scsi_controller_path, self.scsi_scanner_device_path
757+ )
758+ for device in devices.values():
759+ self.assertEqual(
760+ device.device_id in real_devices, device.is_real_device,
761+ 'Invalid result of UdevDevice.is_real_device for %s '
762+ 'Expected %s, got %s'
763+ % (device.device_id, device.device_id in real_devices,
764+ device.is_real_device))
765+
766+ def test_is_real_device_ide_device_related_nodes(self):
767+ """Test of UdevDevice._is_real_device for IDE related nodes.
768+
769+ An IDE device and its controller are represented by several
770+ nodes which describe different aspects. Only the controller
771+ itself and the node representing the IDE device are
772+ considered to be real devices.
773+ """
774+ devices = self.buildUdevDeviceHierarchy(
775+ self.ide_device_hierarchy_data)
776+ real_devices = (
777+ self.pci_ide_controller_path, self.ide_cdrom_device_path,
778+ )
779+ for device in devices.values():
780+ self.assertEqual(
781+ device.device_id in real_devices, device.is_real_device,
782+ 'Invalid result of UdevDevice.is_real_device for %s '
783+ 'Expected %s, got %s'
784+ % (device.device_id, device.device_id in real_devices,
785+ device.is_real_device))
786+
787+ def test_is_real_device_ata_device_related_nodes(self):
788+ """Test of UdevDevice._is_real_device for IDE related nodes.
789+
790+ An IDE device and its controller are represented by several
791+ nodes which describe different aspects. Only the controller
792+ itself and the node representing the IDE device are
793+ considered to be real devices.
794+ """
795+ devices = self.buildUdevDeviceHierarchy(
796+ self.sata_device_hierarchy_data)
797+ real_devices = (
798+ self.pci_sata_controller_path, self.sata_disk_device_path,
799+ )
800+ for device in devices.values():
801+ self.assertEqual(
802+ device.device_id in real_devices, device.is_real_device,
803+ 'Invalid result of UdevDevice.is_real_device for %s '
804+ 'Expected %s, got %s'
805+ % (device.device_id, device.device_id in real_devices,
806+ device.is_real_device))
807+
808+ def test_is_real_device_usb_storage_device_related_nodes(self):
809+ """Test of UdevDevice._is_real_device for USB storage related nodes.
810+
811+ A USB storage device is represented by several nodes which
812+ describe different aspects. Only the main USB device is
813+ considered to be real devices.
814+ """
815+ devices = self.buildUdevDeviceHierarchy(
816+ self.usb_storage_hierarchy_data)
817+ for device in devices.values():
818+ self.assertEqual(
819+ device.device_id == self.usb_storage_usb_device_path,
820+ device.is_real_device,
821+ 'Invalid result of UdevDevice.is_real_device for %s '
822+ 'Expected %s, got %s'
823+ % (device.device_id,
824+ device.device_id == self.usb_storage_usb_device_path,
825+ device.is_real_device))
826+
827
828 class TestHWDBSubmissionTablePopulation(TestCaseHWDB):
829 """Tests of the HWDB popoluation with submitted data."""