def ParseMultiple(self, stats, file_objects, unused_knowledge_base): # Each file gives us only partial information for a particular PCI device. # Iterate through all the files first to create a dictionary encapsulating # complete information for each of the PCI device on the system. We need # all information for a PCI device before a proto for it can be created. # We will store data in a dictionary of dictionaries that looks like this: # data = { '0000:7f:0d.0': { 'class': '0x088000', # 'vendor': '0x8086', # 'device': '0x0ee1' } } # The key is location of PCI device on system in extended B/D/F notation # and value is a dictionary containing filename:data pairs for each file # returned by artifact collection for that PCI device. # Extended B/D/F is of form "domain:bus:device.function". Compile a regex # so we can use it to skip parsing files that don't match it. hc = r"[0-9A-Fa-f]" bdf_regex = re.compile(r"^%s+:%s+:%s+\.%s+" % (hc, hc, hc, hc)) # This will make sure that when a non-existing 'key' (PCI location) # is accessed for the first time a new 'key':{} pair is auto-created data = collections.defaultdict(dict) for stat, file_obj in zip(stats, file_objects): filename = stat.pathspec.Basename() # Location of PCI device is the name of parent directory of returned file. bdf = stat.pathspec.Dirname().Basename() # Make sure we only parse files that are under a valid B/D/F folder if bdf_regex.match(bdf): # Remove newlines from all files except config. Config contains raw data # so we don't want to touch it even if it has a newline character. file_data = file_obj.read() if filename != "config": file_data = file_data.rstrip("\n") data[bdf][filename] = file_data # Now that we've captured all information for each PCI device. Let's convert # the dictionary into a list of PCIDevice protos. for bdf, bdf_filedata in data.iteritems(): pci_device = rdf_client.PCIDevice() bdf_split = bdf.split(":") df_split = bdf_split[2].split(".") # We'll convert the hex into decimal to store in the protobuf. pci_device.domain = int(bdf_split[0], 16) pci_device.bus = int(bdf_split[1], 16) pci_device.device = int(df_split[0], 16) pci_device.function = int(df_split[1], 16) pci_device.class_id = bdf_filedata.get("class") pci_device.vendor = bdf_filedata.get("vendor") pci_device.vendor_device_id = bdf_filedata.get("device") pci_device.config = bdf_filedata.get("config") yield pci_device
def testPCIDevicesInfoParser(self): """Ensure we can extract PCI devices info.""" # Test when there's data for one PCI device only. test_data1 = { "/sys/bus/pci/devices/0000:00:01.0/vendor": "0x0e00\n", "/sys/bus/pci/devices/0000:00:01.0/class": "0x060400\n", "/sys/bus/pci/devices/0000:00:01.0/device": "0x0e02\n", "/sys/bus/pci/devices/0000:00:01.0/config": "0200" } device_1 = rdf_client.PCIDevice(domain=0, bus=0, device=1, function=0, class_id="0x060400", vendor="0x0e00", vendor_device_id="0x0e02", config="0200") parsed_results = self._ParsePCIDeviceTestData(test_data1) self._MatchPCIDeviceResultToExpected(parsed_results, [device_1]) # Use raw bytes to test PCI device config works as expected. bytes2 = bytearray([234, 232, 231, 188, 122, 132, 145]) test_data2 = { "/sys/bus/pci/devices/0000:00:00.0/vendor": "0x8086\n", "/sys/bus/pci/devices/0000:00:00.0/class": "0x060000\n", "/sys/bus/pci/devices/0000:00:00.0/device": "0x0e00\n", "/sys/bus/pci/devices/0000:00:00.0/config": bytes2 } device_2 = rdf_client.PCIDevice(domain=0, bus=0, device=0, function=0, class_id="0x060000", vendor="0x8086", vendor_device_id="0x0e00", config=b"\xea\xe8\xe7\xbcz\x84\x91") parsed_results = self._ParsePCIDeviceTestData(test_data2) self._MatchPCIDeviceResultToExpected(parsed_results, [device_2]) # Test for when there's missing data. test_data3 = { "/sys/bus/pci/devices/0000:00:03.0/vendor": "0x0e00\n", "/sys/bus/pci/devices/0000:00:03.0/config": "0030" } device_3 = rdf_client.PCIDevice(domain=0, bus=0, device=3, function=0, vendor="0x0e00", config="0030") parsed_results = self._ParsePCIDeviceTestData(test_data3) self._MatchPCIDeviceResultToExpected(parsed_results, [device_3]) # Test when data contains non-valid B/D/F folders/files. test_data4 = { "/sys/bus/pci/devices/0000:00:05.0/vendor": "0x0e00\n", "/sys/bus/pci/devices/0000:00:05.0/class": "0x060400\n", "/sys/bus/pci/devices/0000:00:05.0/device": "0x0e02\n", "/sys/bus/pci/devices/0000:00:05.0/config": "0200", "/sys/bus/pci/devices/crazyrandomfile/test1": "test1", "/sys/bus/pci/devices/::./test2": "test2", "/sys/bus/pci/devices/00:5.0/test3": "test3" } device_4 = rdf_client.PCIDevice(domain=0, bus=0, device=5, function=0, class_id="0x060400", vendor="0x0e00", vendor_device_id="0x0e02", config="0200") parsed_results = self._ParsePCIDeviceTestData(test_data4) self._MatchPCIDeviceResultToExpected(parsed_results, [device_4]) # Test when there's multiple PCI devices in the test_data. combined_data = test_data1.copy() combined_data.update(test_data3) combined_data.update(test_data4) combined_data.update(test_data2) parsed_results = self._ParsePCIDeviceTestData(combined_data) self._MatchPCIDeviceResultToExpected( parsed_results, [device_1, device_4, device_2, device_3])