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

Proposed by Abel Deuring
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
Reviewer Review Type Date Requested Status
Michael Nelson (community) code Approve
Review via email: mp+13402@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

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.translateScsiBus(). 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.translateScsiBus(). 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.translateScsiBus, we need to create five UdevDevices, so I added a method buildUdevDeviceHierarchy() 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:
  lib/canonical/launchpad/scripts/hwdbsubmissions.py
  lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py

== Pyflakes notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    22: redefinition of unused 'etree' from line 20

== Pylint notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    20: [F0401] Unable to import 'xml.etree.cElementTree' (No module named etree)

This complaint is not related to my changes.

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (27.5 KiB)

> 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/launchpad/scripts/hwdbsubmissions.py'
> --- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-13 21:03:31 +0000
> +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-15 10:53:08 +0000
[snip]
> @@ -2617,6 +2635,29 @@
> """See `BaseDevice`."""
> return self.getVendorOrProductID('product')
>
> + @property
> + def driver_name(self):
> + """See `BaseDevice`."""
> + return self.udev['E'].get('DRIVER')
> +
> + @property
> + def scsi_controller(self):
> + """See `BaseDevice`."""
> + if self.raw_bus != 'scsi_device':
> + return None
> +
> + current_device = self
> + for ancestor_level in range(1, 5):
> + if current_device.parent is None:
> + # 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._logWarning(
> + 'Found a SCSI device without a sufficient number of '
> + 'ancestors: %s' % self.device_id)
> + return None
> + current_device = current_device.parent
> + 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.parent.parent.parent
    except AttributeError:
        return None

Up to you.

>
> class ProcessingLoop(object):
> """An `ITunableLoop` for processing HWDB submissions."""
>
> === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
> --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-13 21:02:35 +0000
> +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-15 10:53:08 +0000
> @@ -539,6 +539,214 @@
> 'HAL property info.bus.')
>
>
> + def test_HALDevice_scsi_controller_usb_storage_device(self):
> + """test of HALDevice.scsi_controller.
> +
> + The physical device is a USB storage device.
> + """
> + devices = [
> + # The main node of the USB storage device.
> + {
> + 'id': 1,
> + 'udi': self.UDI_USB_STORAGE,
> + 'properties': {
> + 'info.bus': ('usb_device', 'str'),
> + },
> + },

Just a style question (the answer to which I'm not certain), based on:

https://dev.launchpad.net/PythonStyleGuide#Multiline%20braces

should this be:

        devices = [{
            # The main node of the USB storage device.
            'id': 1,
            'udi': self.UDI_USB_STORAGE,
            '...

review: Approve (code)
Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (45.5 KiB)

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/launchpad/scripts/hwdbsubmissions.py'
>> --- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-13 21:03:31 +0000
>> +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-15 10:53:08 +0000
> [snip]
>> @@ -2617,6 +2635,29 @@
>> """See `BaseDevice`."""
>> return self.getVendorOrProductID('product')
>>
>> + @property
>> + def driver_name(self):
>> + """See `BaseDevice`."""
>> + return self.udev['E'].get('DRIVER')
>> +
>> + @property
>> + def scsi_controller(self):
>> + """See `BaseDevice`."""
>> + if self.raw_bus != 'scsi_device':
>> + return None
>> +
>> + current_device = self
>> + for ancestor_level in range(1, 5):
>> + if current_device.parent is None:
>> + # 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._logWarning(
>> + 'Found a SCSI device without a sufficient number of '
>> + 'ancestors: %s' % self.device_id)
>> + return None
>> + current_device = current_device.parent
>> + 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.parent.parent.parent
> except AttributeError:
> return None
>
> Up to you.

Right, that's a bit simpler and beter eadable. Changed.

>
>>
>> class ProcessingLoop(object):
>> """An `ITunableLoop` for processing HWDB submissions."""
>>
>> === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
>> --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-13 21:02:35 +0000
>> +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-15 10:53:08 +0000
>> @@ -539,6 +539,214 @@
>> 'HAL property info.bus.')
>>
>>
>> + def test_HALDevice_scsi_controller_usb_storage_device(self):
>> + """test of HALDevice.scsi_controller.
>> +
>> + The physical device is a USB storage device.
>> + """
>> + devices = [
>> + # The main node of the USB storage device.
>> + {
>> + 'id': 1,
>> + 'udi': self.UDI_USB_STORAGE,
>> + 'properties': {
>> + 'info.bus': ('usb_device', 'str'),
>> + },
>> + },
>
> Just a style question (the answer to whic...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
--- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-13 21:03:31 +0000
+++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-15 13:25:24 +0000
@@ -87,12 +87,14 @@
87 'pci': '0x%04x',87 'pci': '0x%04x',
88 'usb_device': '0x%04x',88 'usb_device': '0x%04x',
89 'scsi': '%-8s',89 'scsi': '%-8s',
90 'scsi_device': '%-8s',
90 }91 }
9192
92DB_FORMAT_FOR_PRODUCT_ID = {93DB_FORMAT_FOR_PRODUCT_ID = {
93 'pci': '0x%04x',94 'pci': '0x%04x',
94 'usb_device': '0x%04x',95 'usb_device': '0x%04x',
95 'scsi': '%-16s',96 'scsi': '%-16s',
97 'scsi_device': '%-16s',
96 }98 }
9799
98UDEV_USB_DEVICE_PROPERTIES = set(('DEVTYPE', 'PRODUCT', 'TYPE'))100UDEV_USB_DEVICE_PROPERTIES = set(('DEVTYPE', 'PRODUCT', 'TYPE'))
@@ -1609,6 +1611,11 @@
1609 """The name of the driver contolling this device. May be None."""1611 """The name of the driver contolling this device. May be None."""
1610 raise NotImplementedError1612 raise NotImplementedError
16111613
1614 @property
1615 def scsi_controller(self):
1616 """Return the SCSI host controller for this device."""
1617 raise NotImplementedError
1618
1612 def translateScsiBus(self):1619 def translateScsiBus(self):
1613 """Return the real bus of a device where raw_bus=='scsi'.1620 """Return the real bus of a device where raw_bus=='scsi'.
16141621
@@ -1617,37 +1624,27 @@
1617 for more details. This method determines the real bus1624 for more details. This method determines the real bus
1618 of a device accessed via the kernel's SCSI subsystem.1625 of a device accessed via the kernel's SCSI subsystem.
1619 """1626 """
1620 # While SCSI devices from valid submissions should have a1627 scsi_controller = self.scsi_controller
1621 # parent and a grandparent, we can't be sure for bogus or1628 if scsi_controller is None:
1622 # broken submissions.
1623 parent = self.parent
1624 if parent is None:
1625 self.parser._logWarning(
1626 'Found SCSI device without a parent: %s.' % self.device_id)
1627 return None
1628 grandparent = parent.parent
1629 if grandparent is None:
1630 self.parser._logWarning(
1631 'Found SCSI device without a grandparent: %s.'
1632 % self.device_id)
1633 return None1629 return None
16341630
1635 grandparent_bus = grandparent.raw_bus1631 scsi_controller_bus = scsi_controller.raw_bus
1636 if grandparent_bus == 'pci':1632 if scsi_controller_bus == 'pci':
1637 if (grandparent.pci_class != PCI_CLASS_STORAGE):1633 if (scsi_controller.pci_class != PCI_CLASS_STORAGE):
1638 # This is not a storage class PCI device? This1634 # This is not a storage class PCI device? This
1639 # indicates a bug somewhere in HAL or in the hwdb1635 # indicates a bug somewhere in HAL or in the hwdb
1640 # client, or a fake submission.1636 # client, or a fake submission.
1641 device_class = grandparent.pci_class1637 device_class = scsi_controller.pci_class
1642 self.parser._logWarning(1638 self.parser._logWarning(
1643 'A (possibly fake) SCSI device %s is connected to '1639 'A (possibly fake) SCSI device %s is connected to '
1644 'PCI device %s that has the PCI device class %s; '1640 'PCI device %s that has the PCI device class %s; '
1645 'expected class 1 (storage).'1641 'expected class 1 (storage).'
1646 % (self.device_id, grandparent.device_id, device_class))1642 % (self.device_id, scsi_controller.device_id,
1643 device_class))
1647 return None1644 return None
1648 pci_subclass = grandparent.pci_subclass1645 pci_subclass = scsi_controller.pci_subclass
1649 return self.pci_storage_subclass_hwbus.get(pci_subclass)1646 return self.pci_storage_subclass_hwbus.get(pci_subclass)
1650 elif grandparent_bus == 'usb':1647 elif scsi_controller_bus == 'usb':
1651 # USB storage devices have the following HAL device hierarchy:1648 # USB storage devices have the following HAL device hierarchy:
1652 # - HAL node for the USB device. info.bus == 'usb_device',1649 # - HAL node for the USB device. info.bus == 'usb_device',
1653 # device class == 0, device subclass == 01650 # device class == 0, device subclass == 0
@@ -2357,6 +2354,27 @@
2357 """See `BaseDevice`."""2354 """See `BaseDevice`."""
2358 return self.getVendorOrProductID('product')2355 return self.getVendorOrProductID('product')
23592356
2357 @property
2358 def scsi_controller(self):
2359 """See `BaseDevice`."""
2360 # While SCSI devices from valid submissions should have a
2361 # parent and a grandparent, we can't be sure for bogus or
2362 # broken submissions.
2363 if self.raw_bus != 'scsi':
2364 return None
2365 parent = self.parent
2366 if parent is None:
2367 self.parser._logWarning(
2368 'Found SCSI device without a parent: %s.' % self.device_id)
2369 return None
2370 grandparent = parent.parent
2371 if grandparent is None:
2372 self.parser._logWarning(
2373 'Found SCSI device without a grandparent: %s.'
2374 % self.device_id)
2375 return None
2376 return grandparent
2377
23602378
2361class UdevDevice(BaseDevice):2379class UdevDevice(BaseDevice):
2362 """The representation of a udev device node."""2380 """The representation of a udev device node."""
@@ -2617,6 +2635,29 @@
2617 """See `BaseDevice`."""2635 """See `BaseDevice`."""
2618 return self.getVendorOrProductID('product')2636 return self.getVendorOrProductID('product')
26192637
2638 @property
2639 def driver_name(self):
2640 """See `BaseDevice`."""
2641 return self.udev['E'].get('DRIVER')
2642
2643 @property
2644 def scsi_controller(self):
2645 """See `BaseDevice`."""
2646 if self.raw_bus != 'scsi_device':
2647 return None
2648
2649 # While SCSI devices from valid submissions should have four
2650 # ancestors, we can't be sure for bogus or broken submissions.
2651 try:
2652 controller = self.parent.parent.parent.parent
2653 except AttributeError:
2654 controller = None
2655 if controller is None:
2656 self.parser._logWarning(
2657 'Found a SCSI device without a sufficient number of '
2658 'ancestors: %s' % self.device_id)
2659 return None
2660 return controller
26202661
2621class ProcessingLoop(object):2662class ProcessingLoop(object):
2622 """An `ITunableLoop` for processing HWDB submissions."""2663 """An `ITunableLoop` for processing HWDB submissions."""
26232664
=== modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-13 21:02:35 +0000
+++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-15 13:25:24 +0000
@@ -539,6 +539,214 @@
539 'HAL property info.bus.')539 'HAL property info.bus.')
540540
541541
542 def test_HALDevice_scsi_controller_usb_storage_device(self):
543 """test of HALDevice.scsi_controller.
544
545 The physical device is a USB storage device.
546 """
547 devices = [
548 # The main node of the USB storage device.
549 {
550 'id': 1,
551 'udi': self.UDI_USB_STORAGE,
552 'properties': {
553 'info.bus': ('usb_device', 'str'),
554 },
555 },
556 # The storage interface of the USB device.
557 {
558 'id': 2,
559 'udi': self.UDI_USB_STORAGE_IF0,
560 'properties': {
561 'info.bus': ('usb', 'str'),
562 'info.parent': (self.UDI_USB_STORAGE, 'str'),
563 },
564 },
565 # The fake SCSI host of the storage device. Note that HAL does
566 # _not_ provide the info.bus property.
567 {
568 'id': 3,
569 'udi': self.UDI_USB_STORAGE_SCSI_HOST,
570 'properties': {
571 'info.parent': (self.UDI_USB_STORAGE_IF0, 'str'),
572 },
573 },
574 # The fake SCSI disk.
575 {
576 'id': 3,
577 'udi': self.UDI_USB_STORAGE_SCSI_DEVICE,
578 'properties': {
579 'info.bus': ('scsi', 'str'),
580 'info.parent': (self.UDI_USB_STORAGE_SCSI_HOST, 'str'),
581 },
582 },
583 ]
584 parsed_data = {
585 'hardware': {
586 'hal': {
587 'devices': devices,
588 },
589 },
590 }
591
592 parser = SubmissionParser()
593 parser.buildDeviceList(parsed_data)
594
595 usb_fake_scsi_disk = parser.hal_devices[
596 self.UDI_USB_STORAGE_SCSI_DEVICE]
597 usb_main_device = parser.hal_devices[self.UDI_USB_STORAGE_IF0]
598 self.assertEqual(usb_main_device, usb_fake_scsi_disk.scsi_controller)
599
600 def test_HALDevice_scsi_controller_pci_controller(self):
601 """test of HALDevice.scsi_controller.
602
603 Variant for a SCSI device connected to a PCI controller.
604 """
605 devices = [
606 # The PCI host controller.
607 {
608 'id': 1,
609 'udi': self.UDI_SATA_CONTROLLER,
610 'properties': {
611 'info.bus': ('pci', 'str'),
612 'pci.device_class': (PCI_CLASS_STORAGE, 'int'),
613 'pci.device_subclass': (PCI_SUBCLASS_STORAGE_SATA,
614 'int'),
615 },
616 },
617 # The (fake or real) SCSI host of the storage device.
618 {
619 'id': 2,
620 'udi': self.UDI_SATA_CONTROLLER_SCSI,
621 'properties': {
622 'info.parent': (self.UDI_SATA_CONTROLLER, 'str'),
623 },
624 },
625 # The (possibly fake) SCSI disk.
626 {
627 'id': 3,
628 'udi': self.UDI_SATA_DISK,
629 'properties': {
630 'info.bus': ('scsi', 'str'),
631 'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
632 },
633 },
634 ]
635 parsed_data = {
636 'hardware': {
637 'hal': {
638 'devices': devices,
639 },
640 },
641 }
642
643 parser = SubmissionParser()
644 parser.buildDeviceList(parsed_data)
645
646 scsi_device = parser.hal_devices[self.UDI_SATA_DISK]
647 controller = parser.hal_devices[self.UDI_SATA_CONTROLLER]
648 self.assertEqual(controller, scsi_device.scsi_controller)
649
650 def test_HALDevice_scsi_controller_non_scsi_device(self):
651 """test of HALDevice.scsi_controller.
652
653 Variant for non-SCSI devices.
654 """
655 devices = [
656 {
657 'id': 1,
658 'udi': self.UDI_COMPUTER,
659 'properties': {},
660 },
661 ]
662 parsed_data = {
663 'hardware': {
664 'hal': {
665 'devices': devices,
666 },
667 },
668 }
669
670 parser = SubmissionParser()
671 parser.buildDeviceList(parsed_data)
672
673 device = parser.hal_devices[self.UDI_COMPUTER]
674 self.assertEqual(None, device.scsi_controller)
675
676 def test_HALDevice_scsi_controller_no_grandparent(self):
677 """test of HALDevice.scsi_controller.
678
679 Variant for a SCSI device without a grandparent device.
680 """
681 devices = [
682 # The (fake or real) SCSI host of the storage device.
683 {
684 'id': 1,
685 'udi': self.UDI_SATA_CONTROLLER_SCSI,
686 'properties': {},
687 },
688 # The (possibly fake) SCSI disk.
689 {
690 'id': 2,
691 'udi': self.UDI_SATA_DISK,
692 'properties': {
693 'info.bus': ('scsi', 'str'),
694 'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
695 },
696 },
697 ]
698 parsed_data = {
699 'hardware': {
700 'hal': {
701 'devices': devices,
702 },
703 },
704 }
705
706 parser = SubmissionParser(self.log)
707 parser.submission_key = 'SCSI device without grandparent device'
708 parser.buildDeviceList(parsed_data)
709
710 scsi_device = parser.hal_devices[self.UDI_SATA_DISK]
711 self.assertEqual(None, scsi_device.scsi_controller)
712 self.assertWarningMessage(
713 parser.submission_key,
714 "Found SCSI device without a grandparent: %s."
715 % self.UDI_SATA_DISK)
716
717 def test_HALDevice_scsi_controller_no_parent(self):
718 """test of HALDevice.scsi_controller.
719
720 Variant for a SCSI device without a parent device.
721 """
722 devices = [
723 # The (possibly fake) SCSI disk.
724 {
725 'id': 1,
726 'udi': self.UDI_SATA_DISK,
727 'properties': {
728 'info.bus': ('scsi', 'str'),
729 },
730 },
731 ]
732 parsed_data = {
733 'hardware': {
734 'hal': {
735 'devices': devices,
736 },
737 },
738 }
739
740 parser = SubmissionParser(self.log)
741 parser.submission_key = 'SCSI device without parent device'
742 parser.buildDeviceList(parsed_data)
743
744 scsi_device = parser.hal_devices[self.UDI_SATA_DISK]
745 self.assertEqual(None, scsi_device.scsi_controller)
746 self.assertWarningMessage(
747 parser.submission_key,
748 "Found SCSI device without a parent: %s." % self.UDI_SATA_DISK)
749
542 def testHALDeviceGetRealBus(self):750 def testHALDeviceGetRealBus(self):
543 """Test of HALDevice.real_bus, generic case.751 """Test of HALDevice.real_bus, generic case.
544752
@@ -2585,65 +2793,241 @@
2585 'property not treated as a real device.')2793 'property not treated as a real device.')
25862794
25872795
2588class TestUdevDevice(TestCase):2796class TestUdevDevice(TestCaseHWDB):
2589 """Tests of class UdevDevice."""2797 """Tests of class UdevDevice."""
25902798
2591 layer = BaseLayer2799 def setUp(self):
25922800 """Setup the test environment."""
2593 root_device = {2801 super(TestUdevDevice, self).setUp()
2594 'P': '/devices/LNXSYSTM:00',2802 self.root_device = {
2595 'E': {2803 'P': '/devices/LNXSYSTM:00',
2596 'UDEV_LOG': '3',2804 'E': {
2597 'DEVPATH': '/devices/LNXSYSTM:00',2805 'UDEV_LOG': '3',
2598 'MODALIAS': 'acpi:LNXSYSTM:',2806 'DEVPATH': '/devices/LNXSYSTM:00',
2599 'SUBSYSTEM': 'acpi',2807 'MODALIAS': 'acpi:LNXSYSTM:',
2600 }2808 'SUBSYSTEM': 'acpi',
2601 }2809 }
26022810 }
2603 root_device_dmi_data = {2811
2604 '/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS',2812 self.root_device_dmi_data = {
2605 '/sys/class/dmi/id/product_name': 'LIFEBOOK E8210',2813 '/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS',
2606 }2814 '/sys/class/dmi/id/product_name': 'LIFEBOOK E8210',
26072815 }
2608 pci_device_data = {2816
2609 'P': '/devices/pci0000:00/0000:00:1f.2',2817 self.pci_device_data = {
2610 'E': {2818 'P': '/devices/pci0000:00/0000:00:1f.2',
2611 'PCI_CLASS': '10602',2819 'E': {
2612 'PCI_ID': '8086:27C5',2820 'PCI_CLASS': '10602',
2613 'PCI_SUBSYS_ID': '10CF:1387',2821 'PCI_ID': '8086:27C5',
2614 'PCI_SLOT_NAME': '0000:00:1f.2',2822 'PCI_SUBSYS_ID': '10CF:1387',
2615 'SUBSYSTEM': 'pci',2823 'PCI_SLOT_NAME': '0000:00:1f.2',
2616 }2824 'SUBSYSTEM': 'pci',
2617 }2825 'DRIVER': 'ahci',
26182826 }
2619 usb_device_data = {2827 }
2620 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2',2828
2621 'E': {2829 self.usb_device_data = {
2622 'SUBSYSTEM': 'usb',2830 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2',
2623 'DEVTYPE': 'usb_device',2831 'E': {
2624 'PRODUCT': '46d/a01/1013',2832 'SUBSYSTEM': 'usb',
2625 'TYPE': '0/0/0',2833 'DEVTYPE': 'usb_device',
2626 },2834 'PRODUCT': '46d/a01/1013',
2627 }2835 'TYPE': '0/0/0',
26282836 'DRIVER': 'usb',
2629 scsi_device_data = {2837 },
2630 'P': '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0',2838 }
2631 'E': {2839
2632 'SUBSYSTEM': 'scsi',2840 self.pci_scsi_controller_data = {
2633 'DEVTYPE': 'scsi_device',2841 'P': '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0',
2634 },2842 'E': {
2635 }2843 'DRIVER': 'aic7xxx',
26362844 'PCI_CLASS': '10000',
2637 scsi_device_sysfs_data = {2845 'PCI_ID': '9004:6075',
2638 'vendor': 'MATSHITA',2846 'PCI_SUBSYS_ID': '9004:7560',
2639 'model': 'DVD-RAM UJ-841S',2847 'SUBSYSTEM': 'pci',
2640 'type': '5',2848 },
2641 }2849 }
26422850
2643 no_subsystem_device_data = {2851 self.pci_scsi_controller_scsi_side_1 = {
2644 'P': '/devices/pnp0/00:00',2852 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
2645 'E': {}2853 '0000:09:00.0/host6'),
2646 }2854 'E': {
2855 'DEVTYPE': 'scsi_host',
2856 'SUBSYSTEM': 'scsi',
2857 },
2858 }
2859
2860 self.pci_scsi_controller_scsi_side_2 = {
2861 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
2862 '0000:09:00.0/host6/scsi_host/host6'),
2863 'E': {
2864 'SUBSYSTEM': 'scsi_host',
2865 },
2866 }
2867
2868 self.scsi_scanner_target_data = {
2869 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
2870 '0000:09:00.0/host6/target6:0:1'),
2871 'E': {
2872 'DEVTYPE': 'scsi_target',
2873 'SUBSYSTEM': 'scsi'
2874 },
2875 }
2876
2877 self.scsi_scanner_device_data = {
2878 'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
2879 '0000:09:00.0/host6/target6:0:1/6:0:1:0'),
2880 'E': {
2881 'DEVTYPE': 'scsi_device',
2882 'SUBSYSTEM': 'scsi',
2883 },
2884 }
2885
2886 self.scsi_scanner_device_sysfs_data = {
2887 'vendor': 'FUJITSU',
2888 'model': 'fi-5120Cdj',
2889 'type': '6',
2890 }
2891
2892 self.scsi_device_hierarchy_data = [
2893 {'udev_data': self.pci_scsi_controller_data},
2894 {'udev_data': self.pci_scsi_controller_scsi_side_1},
2895 {'udev_data': self.pci_scsi_controller_scsi_side_2},
2896 {'udev_data': self.scsi_scanner_target_data},
2897 {
2898 'udev_data': self.scsi_scanner_device_data,
2899 'sysfs_data': self.scsi_scanner_device_sysfs_data,
2900 },
2901 ]
2902
2903 self.pci_ide_controller = {
2904 'P': '/devices/pci0000:00/0000:00:1f.1',
2905 'E': {
2906 'DRIVER': 'ata_piix',
2907 'PCI_CLASS': '1018A',
2908 'PCI_ID': '8086:27DF',
2909 'PCI_SUBSYS_ID': '10CF:1385',
2910 'SUBSYSTEM': 'pci',
2911 },
2912 }
2913
2914 self.pci_ide_controller_scsi_side_1 = {
2915 'P': '/devices/pci0000:00/0000:00:1f.1/host4',
2916 'E': {
2917 'DEVTYPE': 'scsi_host',
2918 'SUBSYSTEM': 'scsi',
2919 },
2920 }
2921
2922 self.pci_ide_controller_scsi_side_2 = {
2923 'P': '/devices/pci0000:00/0000:00:1f.1/host4/scsi_host/host4',
2924 'E': {
2925 'SUBSYSTEM': 'scsi_host',
2926 },
2927 }
2928
2929 self.ide_device_target_data = {
2930 'P': '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0',
2931 'E': {
2932 'DEVTYPE': 'scsi_target',
2933 'SUBSYSTEM': 'scsi',
2934 },
2935 }
2936
2937 self.ide_cdrom_device_data = {
2938 'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
2939 '4:0:0:0'),
2940 'E': {
2941 'SUBSYSTEM': 'scsi',
2942 'DEVTYPE': 'scsi_device',
2943 'DRIVER': 'sr',
2944 },
2945 }
2946
2947 self.ide_cdrom_device_sysfs_data = {
2948 'vendor': 'MATSHITA',
2949 'model': 'DVD-RAM UJ-841S',
2950 'type': '5',
2951 }
2952
2953 self.ide_device_hierarchy_data = [
2954 {'udev_data': self.pci_ide_controller},
2955 {'udev_data': self.pci_ide_controller_scsi_side_1},
2956 {'udev_data': self.pci_ide_controller_scsi_side_2},
2957 {'udev_data': self.ide_device_target_data},
2958 {
2959 'udev_data': self.ide_cdrom_device_data,
2960 'sysfs_data': self.ide_cdrom_device_sysfs_data,
2961 },
2962 ]
2963
2964 self.usb_storage_usb_interface = {
2965 'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0',
2966 'E': {
2967 'DRIVER': 'usb-storage',
2968 'PRODUCT': '1307/163/100',
2969 'TYPE': '0/0/0',
2970 'INTERFACE': '8/6/80',
2971 'SUBSYSTEM': 'usb',
2972 },
2973 }
2974
2975 self.usb_storage_scsi_host_1 = {
2976 'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7',
2977 'E': {
2978 'DEVTYPE': 'scsi_host',
2979 'SUBSYSTEM': 'scsi',
2980 },
2981 }
2982
2983 self.usb_storage_scsi_host_2 = {
2984 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
2985 'scsi_host/host7'),
2986 'E': {
2987 'SUBSYSTEM': 'scsi_host',
2988 },
2989 }
2990
2991 self.usb_storage_scsi_target = {
2992 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
2993 'target7:0:0'),
2994 'E': {
2995 'DEVTYPE': 'scsi_target',
2996 'SUBSYSTEM': 'scsi',
2997 },
2998 }
2999
3000 self.usb_storage_scsi_device = {
3001 'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
3002 'target7:0:0/7:0:0:0'),
3003 'E': {
3004 'DEVTYPE': 'scsi_device',
3005 'DRIVER': 'sd',
3006 'SUBSYSTEM': 'scsi',
3007 },
3008 }
3009
3010 self.usb_storage_scsi_device_sysfs = {
3011 'vendor': 'Ut163',
3012 'model': 'USB2FlashStorage',
3013 'type': '0',
3014 }
3015
3016 self.usb_storage_hierarchy_data = [
3017 {'udev_data': self.usb_storage_usb_interface},
3018 {'udev_data': self.usb_storage_scsi_host_1},
3019 {'udev_data': self.usb_storage_scsi_host_2},
3020 {'udev_data': self.usb_storage_scsi_target},
3021 {
3022 'udev_data': self.usb_storage_scsi_device,
3023 'sysfs_data': self.usb_storage_scsi_device_sysfs,
3024 },
3025 ]
3026
3027 self.no_subsystem_device_data = {
3028 'P': '/devices/pnp0/00:00',
3029 'E': {}
3030 }
26473031
2648 def test_device_id(self):3032 def test_device_id(self):
2649 """Test of UdevDevice.device_id."""3033 """Test of UdevDevice.device_id."""
@@ -2790,7 +3174,8 @@
2790 def test_is_scsi_device(self):3174 def test_is_scsi_device(self):
2791 """Test of UdevDevice.is_scsi_device."""3175 """Test of UdevDevice.is_scsi_device."""
2792 device = UdevDevice(3176 device = UdevDevice(
2793 None, self.scsi_device_data, self.scsi_device_sysfs_data)3177 None, self.scsi_scanner_device_data,
3178 self.scsi_scanner_device_sysfs_data)
2794 self.assertTrue(device.is_scsi_device)3179 self.assertTrue(device.is_scsi_device)
27953180
2796 device = UdevDevice(None, self.root_device)3181 device = UdevDevice(None, self.root_device)
@@ -2799,16 +3184,18 @@
2799 def test_scsi_vendor(self):3184 def test_scsi_vendor(self):
2800 """Test of UdevDevice.scsi_vendor."""3185 """Test of UdevDevice.scsi_vendor."""
2801 device = UdevDevice(3186 device = UdevDevice(
2802 None, self.scsi_device_data, self.scsi_device_sysfs_data, None)3187 None, self.scsi_scanner_device_data,
2803 self.assertEqual('MATSHITA', device.scsi_vendor)3188 self.scsi_scanner_device_sysfs_data)
3189 self.assertEqual('FUJITSU', device.scsi_vendor)
2804 device = UdevDevice(None, self.root_device)3190 device = UdevDevice(None, self.root_device)
2805 self.assertEqual(None, device.scsi_vendor)3191 self.assertEqual(None, device.scsi_vendor)
28063192
2807 def test_scsi_model(self):3193 def test_scsi_model(self):
2808 """Test of UdevDevice.scsi_model."""3194 """Test of UdevDevice.scsi_model."""
2809 device = UdevDevice(3195 device = UdevDevice(
2810 None, self.scsi_device_data, self.scsi_device_sysfs_data)3196 None, self.scsi_scanner_device_data,
2811 self.assertEqual('DVD-RAM UJ-841S', device.scsi_model)3197 self.scsi_scanner_device_sysfs_data)
3198 self.assertEqual('fi-5120Cdj', device.scsi_model)
28123199
2813 device = UdevDevice(None, self.root_device)3200 device = UdevDevice(None, self.root_device)
2814 self.assertEqual(None, device.scsi_model)3201 self.assertEqual(None, device.scsi_model)
@@ -2847,10 +3234,10 @@
2847 self.assertEqual('Unknown', device.getVendorOrProduct('product'))3234 self.assertEqual('Unknown', device.getVendorOrProduct('product'))
28483235
2849 device = UdevDevice(3236 device = UdevDevice(
2850 None, self.scsi_device_data, self.scsi_device_sysfs_data)3237 None, self.scsi_scanner_device_data,
2851 self.assertEqual('MATSHITA', device.getVendorOrProduct('vendor'))3238 self.scsi_scanner_device_sysfs_data)
2852 self.assertEqual(3239 self.assertEqual('FUJITSU', device.getVendorOrProduct('vendor'))
2853 'DVD-RAM UJ-841S', device.getVendorOrProduct('product'))3240 self.assertEqual('fi-5120Cdj', device.getVendorOrProduct('product'))
28543241
2855 device = UdevDevice(None, self.no_subsystem_device_data)3242 device = UdevDevice(None, self.no_subsystem_device_data)
2856 self.assertEqual(None, device.getVendorOrProduct('vendor'))3243 self.assertEqual(None, device.getVendorOrProduct('vendor'))
@@ -2888,10 +3275,10 @@
2888 self.assertEqual(0xa01, device.getVendorOrProductID('product'))3275 self.assertEqual(0xa01, device.getVendorOrProductID('product'))
28893276
2890 device = UdevDevice(3277 device = UdevDevice(
2891 None, self.scsi_device_data, self.scsi_device_sysfs_data)3278 None, self.scsi_scanner_device_data,
2892 self.assertEqual('MATSHITA', device.getVendorOrProductID('vendor'))3279 self.scsi_scanner_device_sysfs_data)
2893 self.assertEqual(3280 self.assertEqual('FUJITSU', device.getVendorOrProductID('vendor'))
2894 'DVD-RAM UJ-841S', device.getVendorOrProductID('product'))3281 self.assertEqual('fi-5120Cdj', device.getVendorOrProductID('product'))
28953282
2896 device = UdevDevice(3283 device = UdevDevice(
2897 None, self.no_subsystem_device_data)3284 None, self.no_subsystem_device_data)
@@ -2910,6 +3297,131 @@
2910 None, self.root_device, None, self.root_device_dmi_data)3297 None, self.root_device, None, self.root_device_dmi_data)
2911 self.assertEqual('LIFEBOOK E8210', device.product_id)3298 self.assertEqual('LIFEBOOK E8210', device.product_id)
29123299
3300 def test_vendor_id_for_db(self):
3301 """Test of UdevDevice.vendor_id_for_db."""
3302 device = UdevDevice(
3303 None, self.root_device, None, self.root_device_dmi_data)
3304 self.assertEqual('FUJITSU SIEMENS', device.vendor_id_for_db)
3305
3306 device = UdevDevice(None, self.pci_device_data)
3307 self.assertEqual('0x8086', device.vendor_id_for_db)
3308
3309 device = UdevDevice(None, self.usb_device_data)
3310 self.assertEqual('0x046d', device.vendor_id_for_db)
3311
3312 device = UdevDevice(
3313 None, self.scsi_scanner_device_data,
3314 self.scsi_scanner_device_sysfs_data)
3315 self.assertEqual('FUJITSU ', device.vendor_id_for_db)
3316
3317 def test_product_id_for_db(self):
3318 """Test of UdevDevice.product_id_for_db."""
3319 device = UdevDevice(
3320 None, self.root_device, None, self.root_device_dmi_data)
3321 self.assertEqual('LIFEBOOK E8210', device.product_id_for_db)
3322
3323 device = UdevDevice(None, self.pci_device_data)
3324 self.assertEqual('0x27c5', device.product_id_for_db)
3325
3326 device = UdevDevice(None, self.usb_device_data)
3327 self.assertEqual('0x0a01', device.product_id_for_db)
3328
3329 device = UdevDevice(
3330 None, self.scsi_scanner_device_data,
3331 self.scsi_scanner_device_sysfs_data)
3332 self.assertEqual('fi-5120Cdj ', device.product_id_for_db)
3333
3334 def test_driver_name(self):
3335 """Test of UdevDevice.driver_name."""
3336 device = UdevDevice(None, self.pci_device_data)
3337 self.assertEqual('ahci', device.driver_name)
3338
3339 device = UdevDevice(
3340 None, self.root_device, None, self.root_device_dmi_data)
3341 self.assertEqual(None, device.driver_name)
3342
3343 def buildUdevDeviceHierarchy(self, device_data, parser=None):
3344 """Build a UdevDevice hierarchy from device_data.
3345
3346 :param device_data: A sequence of arguments that are passed
3347 to the UdevDevice constructor. Each element must be a
3348 dictionary that can be used as a **kwargs argument.
3349
3350 Element N of the sequence is the parent of element N+1.
3351 :param parser: A SubmissionParser instance to be passed to
3352 the constructor of UdevDevice.
3353 """
3354 parent = None
3355 devices = []
3356 for kwargs in device_data:
3357 device = UdevDevice(parser, **kwargs)
3358 devices.append(device)
3359 if parent is not None:
3360 parent.addChild(device)
3361 parent = device
3362 return devices
3363
3364 def test_scsi_controller(self):
3365 """Test of UdevDevice.scsi_controller for a PCI controller."""
3366 devices = self.buildUdevDeviceHierarchy(
3367 self.scsi_device_hierarchy_data)
3368 controller = devices[0]
3369 scsi_device = devices[-1]
3370 self.assertEqual(controller, scsi_device.scsi_controller)
3371
3372 def test_scsi_controller_insufficient_anchestors(self):
3373 """Test of UdevDevice.scsi_controller for a PCI controller.
3374
3375 If a SCSI device does not have a sufficient number of ancestors,
3376 UdevDevice.scsi_controller returns None.
3377 """
3378 parser = SubmissionParser(self.log)
3379 parser.submission_key = 'UdevDevice.scsi_controller ancestor missing'
3380 devices = self.buildUdevDeviceHierarchy(
3381 self.scsi_device_hierarchy_data[1:], parser)
3382 scsi_device = devices[-1]
3383 self.assertEqual(None, scsi_device.scsi_controller)
3384 self.assertWarningMessage(
3385 parser.submission_key,
3386 'Found a SCSI device without a sufficient number of ancestors: '
3387 '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0/'
3388 'host6/target6:0:1/6:0:1:0')
3389
3390 def test_scsi_controller_no_scsi_device(self):
3391 """Test of UdevDevice.scsi_controller for a PCI controller.
3392
3393 For non-SCSI devices, this property is None.
3394 """
3395 device = UdevDevice(None, self.pci_device_data)
3396 self.assertEqual(None, device.scsi_controller)
3397
3398 def test_translateScsiBus_real_scsi_device(self):
3399 """Test of UdevDevice.translateScsiBus() with a real SCSI device."""
3400 devices = self.buildUdevDeviceHierarchy(
3401 self.scsi_device_hierarchy_data)
3402 scsi_device = devices[-1]
3403 self.assertEqual(
3404 HWBus.SCSI, scsi_device.translateScsiBus())
3405
3406 def test_translateScsiBus_ide_device(self):
3407 """Test of UdevDevice.translateScsiBus() with an IDE device."""
3408 devices = self.buildUdevDeviceHierarchy(
3409 self.ide_device_hierarchy_data)
3410 ide_device = devices[-1]
3411 self.assertEqual(HWBus.IDE, ide_device.translateScsiBus())
3412
3413 def test_translateScsiBus_usb_device(self):
3414 """Test of UdevDevice.translateScsiBus() with a USB device."""
3415 devices = self.buildUdevDeviceHierarchy(
3416 self.usb_storage_hierarchy_data)
3417 usb_scsi_device = devices[-1]
3418 self.assertEqual(None, usb_scsi_device.translateScsiBus())
3419
3420 def test_translateScsiBus_non_scsi_device(self):
3421 """Test of UdevDevice.translateScsiBus() for a non-SCSI device."""
3422 device = UdevDevice(None, self.root_device)
3423 self.assertEqual(None, device.translateScsiBus())
3424
29133425
2914class TestHWDBSubmissionTablePopulation(TestCaseHWDB):3426class TestHWDBSubmissionTablePopulation(TestCaseHWDB):
2915 """Tests of the HWDB popoluation with submitted data."""3427 """Tests of the HWDB popoluation with submitted data."""