Skip to content

Commit e1aa4fe

Browse files
Arushi-07copybara-github
authored andcommitted
Fix nvme device info for linux machines
PiperOrigin-RevId: 833917856
1 parent 189cc0c commit e1aa4fe

File tree

2 files changed

+57
-37
lines changed

2 files changed

+57
-37
lines changed

perfkitbenchmarker/linux_virtual_machine.py

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import abc
3030
import collections
3131
import copy
32-
import json
32+
import io
3333
import logging
3434
import os
3535
import posixpath
@@ -41,7 +41,7 @@
4141
import uuid
4242

4343
from absl import flags
44-
from packaging import version as packaging_version
44+
import pandas as pd
4545
from perfkitbenchmarker import background_tasks
4646
from perfkitbenchmarker import disk
4747
from perfkitbenchmarker import errors
@@ -2220,41 +2220,27 @@ def GetNVMEDeviceInfo(self):
22202220
"""Get the NVME disk device info, by querying the VM."""
22212221
self.InstallPackages('nvme-cli')
22222222
version_str, _ = self.RemoteCommand('sudo nvme --version')
2223-
version_num = version_str.split()[2]
2224-
# TODO(arushigaur): Version check can be removed and we can just parse
2225-
# the raw output.
2226-
if packaging_version.parse(version_num) >= packaging_version.parse(
2227-
'1.5'
2228-
) and packaging_version.parse(version_num) < packaging_version.parse(
2229-
'2.11'
2230-
):
2231-
stdout, _ = self.RemoteCommand('sudo nvme list --output-format json')
2232-
if not stdout:
2233-
return []
2234-
response = json.loads(stdout)
2235-
return response.get('Devices', [])
2236-
else:
2237-
# custom parsing for older OSes that do not ship nvme-cli ver 1.5+.
2238-
response = []
2239-
stdout, _ = self.RemoteCommand('sudo nvme list')
2240-
if 'No NVMe devices detected' in stdout:
2241-
return []
2242-
rows = stdout.splitlines()
2243-
delimiter_row = rows[1] # row 0 is the column headers
2244-
delimiter_index = [0] + [
2245-
i for i in range(len(delimiter_row)) if delimiter_row[i] == ' '
2246-
]
2247-
for row in rows[2:]:
2248-
device = {}
2249-
device_info = [
2250-
row[i:j]
2251-
for i, j in zip(delimiter_index, delimiter_index[1:] + [None])
2252-
]
2253-
device['DevicePath'] = device_info[0].strip()
2254-
device['SerialNumber'] = device_info[1].strip()
2255-
device['ModelNumber'] = device_info[2].strip()
2256-
response.append(device)
2257-
return response
2223+
logging.info('nvme-cli version: %s', version_str.split()[2])
2224+
response = []
2225+
stdout, _ = self.RemoteCommand('sudo nvme list')
2226+
if 'No NVMe devices detected' in stdout:
2227+
return []
2228+
rows = stdout.splitlines()
2229+
header_row = rows[0]
2230+
delimiter_row = rows[1]
2231+
col_spans = [
2232+
(m.start(), m.end()) for m in re.finditer(r'-+', delimiter_row)
2233+
]
2234+
data = '\n'.join([header_row] + rows[2:])
2235+
df_colspecs = pd.read_fwf(io.StringIO(data), colspecs=col_spans)
2236+
df_rows = df_colspecs.to_dict(orient='records')
2237+
for row in df_rows:
2238+
response.append({
2239+
'DevicePath': row.get('Node'),
2240+
'SerialNumber': row.get('SN'),
2241+
'ModelNumber': row.get('Model'),
2242+
})
2243+
return response
22582244

22592245
def GenerateAndCaptureLogs(self) -> list[str]:
22602246
"""Generates and/or captures logs for this VM and returns the paths.

tests/linux_virtual_machine_test.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,40 @@ def testDisableCstatesErrorCases(
854854
vm._DisableCstates()
855855
self.assertNotIn('disabled_cstates', vm.os_metadata)
856856

857+
@mock.patch.object(
858+
pkb_common_test_case.TestLinuxVirtualMachine, 'RemoteCommand'
859+
)
860+
def test_get_nvme_device_info_with_devices(self, mock_remote_command):
861+
vm = CreateTestLinuxVm()
862+
nvme_list_output = """Node Generic SN Model Namespace Usage Format FW Rev
863+
--------------------- --------------------- -------------------- ---------------------------------------- ---------- -------------------------- ---------------- --------
864+
/dev/nvme0n1 /dev/ng0n1 vol06948076c9c135a44 Amazon Elastic Block Store 0x1 8.59 GB / 8.59 GB 512 B + 0 B 1.0
865+
/dev/nvme1n1 /dev/ng1n1 vol06d22d4fd149a2e68 Amazon Elastic Block Store 0x1 10.74 GB / 10.74 GB 512 B + 0 B 1.0
866+
"""
867+
mock_remote_command.side_effect = [
868+
('nvme version 2.13', ''), # nvme --version
869+
(nvme_list_output, ''), # nvme list
870+
]
871+
872+
expected_result = [
873+
{
874+
'DevicePath': '/dev/nvme0n1',
875+
'SerialNumber': 'vol06948076c9c135a44',
876+
'ModelNumber': 'Amazon Elastic Block Store',
877+
},
878+
{
879+
'DevicePath': '/dev/nvme1n1',
880+
'SerialNumber': 'vol06d22d4fd149a2e68',
881+
'ModelNumber': 'Amazon Elastic Block Store',
882+
},
883+
]
884+
885+
result = vm.GetNVMEDeviceInfo()
886+
887+
self.assertEqual(result, expected_result)
888+
mock_remote_command.assert_any_call('sudo nvme --version')
889+
mock_remote_command.assert_any_call('sudo nvme list')
890+
857891

858892
class RangeListUtilTest(parameterized.TestCase):
859893

0 commit comments

Comments
 (0)