Repository: RedHatInsights/insights-core

Issue:
[combiners.lspci] KeyError: '00:00.0
Error:
```
  File "/home/psachin/source/insights/insights-core/insights/combiners/lspci.py", line 122, in __init__
    dev_k = lspci_k.data[dev['Slot']].copy()
KeyError: '00:00.0'
```

The key **dev[Slot]** from `lspci_vmmkn` is not the entire value found in `lspci_k`

```
>>> lspci_k.data
{'0000:00:00.0': {'Dev_Details': 'Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (rev 03)'}, '0000:00:07.0': {'Dev_Details': 'ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 01)', 'Subsystem': 'Microsoft Corporation Device 0000'}, '0000:00:07.1': {'Dev_Details': 'IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)', 'Kernel driver in use': 'ata_piix', 'Kernel modules': 'ata_piix, pata_acpi, ata_generic'}, '0000:00:07.3': {'Dev_Details': 'Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 02)', 'Kernel modules': 'i2c_piix4'}, '0000:00:08.0': {'Dev_Details': 'VGA compatible controller: Microsoft Corporation Hyper-V virtual VGA', 'Kernel driver in use': 'hyperv_fb', 'Kernel modules': 'hyperv_fb'}, '1a06:00:02.0': {'Dev_Details': 'Ethernet controller: Mellanox Technologies MT27710 Family [ConnectX-4 Lx Virtual Function] (rev 80)', 'Subsystem': 'Mellanox Technologies Device 0190', 'Kernel driver in use': 'mlx5_core', 'Kernel modules': 'mlx5_core'}}

>>> lspci_vmmkn
[{'Slot': '00:00.0', 'Class': '0600', 'Vendor': '8086', 'Device': '7192', 'Rev': '03'}, {'Slot': '00:07.0', 'Class': '0601', 'Vendor': '8086', 'Device': '7110', 'SVendor': '1414', 'SDevice': '0000', 'Rev': '01'}, {'Slot': '00:07.1', 'Class': '0101', 'Vendor': '8086', 'Device': '7111', 'Rev': '01', 'ProgIf': '80', 'Driver': 'ata_piix', 'Module': ['ata_piix', 'pata_acpi', 'ata_generic']}, {'Slot': '00:07.3', 'Class': '0680', 'Vendor': '8086', 'Device': '7113', 'Rev': '02', 'Module': ['i2c_piix4']}, {'Slot': '00:08.0', 'Class': '0300', 'Vendor': '1414', 'Device': '5353', 'Driver': 'hyperv_fb', 'Module': ['hyperv_fb']}, {'Slot': '1a06:00:02.0', 'Class': '0200', 'Vendor': '15b3', 'Device': '1016', 'SVendor': '15b3', 'SDevice': '0190', 'PhySlot': '1', 'Rev': '80', 'Driver': 'mlx5_core', 'Module': ['mlx5_core'], 'NUMANode': '0'}]
```


Discussion:
Would you please provide a case/step which could reproduce this issue for the debugging/fixing convenience. Thanks.

Resolving patch:
diff --git a/insights/combiners/lspci.py b/insights/combiners/lspci.py
--- a/insights/combiners/lspci.py
+++ b/insights/combiners/lspci.py
@@ -17,6 +17,12 @@ class LsPci(list):
     Combines the Parser LsPci of ``/sbin/lspci -k`` command and Parser
     LsPciVmmkn of ``/sbin/lspci -vmmkn`` command.
 
+    .. note::
+        In case the ``lspci -k`` sometimes outputs the `Slot` in the full
+        format of ``domain:bus:device.function``, and the ``lspci -k`` is more
+        common than ``lspci -vmmkn``, so this combiner will take the `Slot` of
+        the `lspci -k` as the key.
+
     Typical output of the ``lspci -k`` command is::
 
         00:00.0 Host bridge: Intel Corporation Haswell-ULT DRAM Controller (rev 09)
@@ -119,12 +125,17 @@ def __init__(self, lspci_k, lspci_vmmkn):
                 dev = dev.copy()
                 if lspci_k and dev['Slot'] in lspci_k:
                     # use the local copy to prevent from writing back to the parser
-                    dev_k = lspci_k.data[dev['Slot']].copy()
+                    dev_k = [v for v in lspci_k.data.values() if v['Slot'].endswith(dev['Slot'])][0].copy()
+                    # Since the 'lspci -k' is a more common command than the
+                    #  'lspci -vmmkn', the following line should be commented
+                    #  out to use the 'Slot' in 'lspci -k' as the 'Slot' in
+                    #  this combiner:
+                    # dev_k.pop('Slot') if 'Slot' in dev_k else None
                     dev_k.pop('Kernel driver in use') if 'Kernel driver in use' in dev_k else None
                     dev_k.pop('Kernel modules') if 'Kernel modules' in dev_k else None
                     dev.update(dev_k)
                 self.append(dev)
-            self._pci_dev_list = lspci_vmmkn.pci_dev_list
+            self._pci_dev_list = (lspci_k if lspci_k else lspci_vmmkn).pci_dev_list
         else:
             for dev in lspci_k.data.values():
                 # use the local copy to prevent from writing back to the parser


Test patch:
diff --git a/insights/combiners/tests/test_lspci.py b/insights/combiners/tests/test_lspci.py
--- a/insights/combiners/tests/test_lspci.py
+++ b/insights/combiners/tests/test_lspci.py
@@ -95,6 +95,77 @@
 Module: snd_hda_intel
 """
 
+LSPCI_K_LONG_SLOT = """
+0000:00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (rev 03)
+0000:00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 01)
+        Subsystem: Microsoft Corporation Device 0000
+0000:00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
+        Kernel driver in use: ata_piix
+        Kernel modules: ata_piix, pata_acpi, ata_generic
+0000:00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 02)
+        Kernel modules: i2c_piix4
+0000:00:08.0 VGA compatible controller: Microsoft Corporation Hyper-V virtual VGA
+        Kernel driver in use: hyperv_fb
+        Kernel modules: hyperv_fb
+1a06:00:02.0 Ethernet controller: Mellanox Technologies MT27710 Family [ConnectX-4 Lx Virtual Function] (rev 80)
+        Subsystem: Mellanox Technologies Device 0190
+        Kernel driver in use: mlx5_core
+        Kernel modules: mlx5_core
+"""
+
+LSPCI_VMMKN_LONG_SLOT = """
+Slot:   00:00.0
+Class:  0600
+Vendor: 8086
+Device: 7192
+Rev:    03
+
+Slot:   00:07.0
+Class:  0601
+Vendor: 8086
+Device: 7110
+SVendor:        1414
+SDevice:        0000
+Rev:    01
+
+Slot:   00:07.1
+Class:  0101
+Vendor: 8086
+Device: 7111
+Rev:    01
+ProgIf: 80
+Driver: ata_piix
+Module: ata_piix
+Module: pata_acpi
+Module: ata_generic
+
+Slot:   00:07.3
+Class:  0680
+Vendor: 8086
+Device: 7113
+Rev:    02
+Module: i2c_piix4
+
+Slot:   00:08.0
+Class:  0300
+Vendor: 1414
+Device: 5353
+Driver: hyperv_fb
+Module: hyperv_fb
+
+Slot:   1a06:00:02.0
+Class:  0200
+Vendor: 15b3
+Device: 1016
+SVendor:        15b3
+SDevice:        0190
+PhySlot:        1
+Rev:    80
+Driver: mlx5_core
+Module: mlx5_core
+NUMANode:       0
+"""
+
 
 def test_lspci_k():
     lspci_k = LsPciParser(context_wrap(LSPCI_K))
@@ -164,6 +235,42 @@ def test_lspci_both():
             'Driver', 'Module', 'Rev', 'SDevice', 'SVendor', 'Slot', 'Vendor'])
 
 
+def test_lspci_both_long_slot():
+    lspci_vmmkn = LsPciVmmkn(context_wrap(LSPCI_VMMKN_LONG_SLOT))
+    lspci_k = LsPciParser(context_wrap(LSPCI_K_LONG_SLOT))
+    lspci = LsPci(lspci_k, lspci_vmmkn)
+    assert sorted(lspci.pci_dev_list) == ['0000:00:00.0', '0000:00:07.0', '0000:00:07.1', '0000:00:07.3', '0000:00:08.0', '1a06:00:02.0']
+    assert lspci.search(Dev_Details__contains='PIIX4 ISA') == [
+        {
+            'Slot': '0000:00:07.0', 'Class': '0601', 'Vendor': '8086',
+            'Device': '7110', 'SVendor': '1414', 'SDevice': '0000',
+            'Rev': '01',
+            'Subsystem': 'Microsoft Corporation Device 0000',
+            'Dev_Details': 'ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 01)'
+        }
+    ]
+    assert lspci.search(Slot='1a06:00:02.0') == [
+        {
+            'Slot': '1a06:00:02.0', 'Class': '0200', 'Vendor': '15b3',
+            'Device': '1016', 'SVendor': '15b3', 'SDevice': '0190',
+            'Rev': '80', 'Driver': 'mlx5_core', 'PhySlot': '1',
+            'Module': ['mlx5_core'], 'NUMANode': '0',
+            'Subsystem': 'Mellanox Technologies Device 0190',
+            'Dev_Details': 'Ethernet controller: Mellanox Technologies MT27710 Family [ConnectX-4 Lx Virtual Function] (rev 80)'
+        }
+    ]
+    # Make sure the original parsers are untouched
+    assert sorted(lspci_k.pci_dev_list) == ['0000:00:00.0', '0000:00:07.0',
+            '0000:00:07.1', '0000:00:07.3', '0000:00:08.0', '1a06:00:02.0']
+    assert lspci_k.pci_dev_details('0000:00:00.0').get('Module') is None
+    assert lspci_k.pci_dev_details('0000:00:08.0').get('Kernel driver in use') == 'hyperv_fb'
+    assert sorted(lspci_vmmkn.pci_dev_list) == ['00:00.0', '00:07.0', '00:07.1', '00:07.3', '00:08.0', '1a06:00:02.0']
+    assert sorted(lspci_vmmkn[0].keys()) == sorted(['Class', 'Device', 'Rev', 'Slot', 'Vendor'])
+    assert sorted(lspci_vmmkn[-1].keys()) == sorted(['Class', 'Device',
+            'Driver', 'Module', 'Rev', 'SDevice', 'SVendor', 'Slot', 'Vendor',
+            'PhySlot', 'NUMANode'])
+
+
 def test_doc_examples():
     lspci_vmmkn = LsPciVmmkn(context_wrap(LSPCI_VMMKN))
     lspci_k = LsPciParser(context_wrap(LSPCI_K))