Introduction

This Python library can assist in collecting disk information on Linux. In more details, it can:

  • collect information about a specific disk

  • explore all existing disks in the system

  • translate between traditional and persistent disk names

  • read disk temperature

  • read SMART attributes of a disk

  • read partition list of a disk

  • read raw file system information of a disk

Installation

Standard installation from pypi:

pip install diskinfo

The library has the following run-time requirements:

  • Python version >= 3.10

  • for reading SMART data with get_smart_data() method, the smartmontools package is required

  • for reading disk temperature with get_temperature() method, the following dependencies needs to be considered:

    Disk type

    Requirement

    SATA HDD/SSD

    drivetemp kernel module (Linux kernel version 5.6+) has to be loaded

    SCSI/ATA HDD

    smartmontools has to be installed

    NVME

    none

  • optionally, for the demo Rich Python library is required

Demo

The library contains a demo application with multiple screens. To run the demo, execute the following commands:

pip install rich

The first demo screen will list the explored disks:

python -m diskinfo.demo
https://github.com/petersulyok/diskinfo/raw/main/docs/diskinfo_rich_demo.png

The second demo screen will display the attributes of a specified disk:

python -m diskinfo.demo sdb
https://github.com/petersulyok/diskinfo/raw/main/docs/diskinfo_rich_demo_2.png

The third demo screen will display the SMART attributes of a specified disk:

python -m diskinfo.demo nvme0n1 -s
https://github.com/petersulyok/diskinfo/raw/main/docs/diskinfo_rich_demo_4.png

The fourth demo screen will display the list of partitions on a specified disk:

python -m diskinfo.demo nvme0n1 -p
https://github.com/petersulyok/diskinfo/raw/main/docs/diskinfo_rich_demo_3.png

When applicable, the raw filesystem information is displayed instead of the partition list.

Persistent disk names

Please note that the traditional disk names in Linux are not persistent:

/dev/sda
/dev/sdb

It means they can refer to a different physical disk after a reboot. Read more about this topic at Arch Linux wiki: Persistent block device naming.

On the other hand, there are persistent ways to refer to a disk or block device in Linux:

  1. by-id path: it can be found in /dev/disk/by-id directory and it is constructed with disk serial numbers:

    /dev/disk/by-id/ata-Samsung_SSD_850_PRO_1TB_92837A469FF876
    /dev/disk/by-id/wwn-0x5002539c417223be
    
  2. by-path path: it can be found in /dev/disk/by-path directory and it is constructed with a physical path to the disk:

    /dev/disk/by-path/pci-0000:00:17.0-ata-3
    

There are similar persistent constructions for disk partitions, too.

How to use

The following chapters will present the seven use cases listed in the Introduction chapter above.

Use case 1: collect information about a disk

Disk attributes can be collected with the creation of a Disk class. All disk attributes will be collected at class creation time:

>>> from diskinfo import Disk
>>> d = Disk("sda")

and later the attributes can be accessed with the help of get functions of the class:

>>> d.get_model()
'Samsung SSD 870 QVO 8TB'
>>> d.is_ssd()
True
>>> s, u = d.get_size_in_hrf()
>>> print(f"{s:.1f} {u}")
8.0 TB
>>> d.get_serial()
'S5SXNG0MB01829M'

The Disk class contains the following disk attributes:

Attribute

Description

Sample value

name

Disk name

sda or nvme0n1

path

Disk path

/dev/sda or /dev/nvme0n1

by-id path

Persistent disk path in /dev/disk/by-id directory

by-path path

Persistent disk path in /dev/disk/by-path directory

wwn

World Wide Name

0x5002538c307370ec

model

Disk model

Samsung SSD 850 PRO 1TB

serial number

Disk serial number

S3E2NY0J723218R

firmware

Disk firmware

EXM04B6Q

type

Disk type

HDD, SSD or NVME

size

Disk size in 512-byte blocks

device ID

Disk device ID, in ‘major:minor’ form

8:0

physical block size

Disk physical block size in bytes

512 or 4096

logical block size

Disk logical block size in bytes

512

partition table type

Type of the partition table on disk

gpt or mbr

partition table uuid

UUID of the partition table on disk

Use case 2: explore disks

Disks can be explored with the creation of the DiskInfo class. During this process all disks will be identified and their attributes will be stored:

>>> from diskinfo import Disk, DiskInfo
>>> di = DiskInfo()

After that, the number of identified disks can be read with the help of get_disk_number() method:

>>> di.get_disk_number()
4

and the list of the disks can be accessed (see more details in get_disk_list() method):

>>> disks = di.get_disk_list(sorting=True)
>>> for d in disks:
>>>     print(d.get_path())
/dev/nvme0n1
/dev/sda
/dev/sdb
/dev/sdc

The caller can also apply filters (i.e. included and excluded disk types) for both functions and can query only a subset of the disks based on one or more specific DiskType. The list of disks can also be sorted.

Use case 3: translate between traditional and persistent disk names

Translation from traditional disk names to persistent ones can be done this way:

>>> from diskinfo import Disk
>>> d = Disk("sda")
>>> d.get_byid_path()
['/dev/disk/by-id/ata-Samsung_SSD_850_PRO_1TB_92837A469FF876', '/dev/disk/by-id/wwn-0x5002539c417223be']
>>> d.get_bypath_path()
['/dev/disk/by-path/pci-0000:00:17.0-ata-3', '/dev/disk/by-path/pci-0000:00:17.0-ata-3.0']
>>> d.get_serial_number()
'92837A469FF876'
>>> d.get_wwn()
'0x5002539c417223be'

In the opposite direction, several unique (persistent) identifiers can be used to initialize Disk class then the traditional disk path or name can be read:

>>> from diskinfo import Disk
>>> d = Disk(byid_name="ata-Samsung_SSD_850_PRO_1TB_92837A469FF876")
>>> d.get_path()
'/dev/sda'
>>> d = Disk(bypath_name="pci-0000:00:17.0-ata-3")
>>> d.get_path()
'/dev/sda'
>>> d = Disk(serial_number="92837A469FF876")
>>> d.get_path()
'/dev/sda'
>>> d = Disk(wwn="0x5002539c417223be")
>>> d.get_name()
'sda'

Use case 4: read disk temperature

After having a Disk class instance, the disk temperature can be read in this way:

>>> from diskinfo import Disk
>>> d = Disk("sda")
>>> d.get_temperature()
28

Please note that the drivetemp kernel module should be loaded for SSDs and HDDs (available from Linux Kernel 5.6+). NVME disks do not require anything.

Use case 5: read disk SMART attributes

After having a Disk class instance, the SMART attributes of the disk can be read with the help of get_smart_data() method.

>>> from diskinfo import Disk, DiskSmartData
>>> d = Disk("sda")
>>> sd = d.get_smart_data()

In case of HDDs, we can skip checking if they are in STANDBY mode:

>>> sd = d.get_smart_data(nocheck=True)
>>> if sd.standby_mode:
...     print("Disk is in STANDBY mode.")
... else:
...     print("Disk is ACTIVE.")
...
Disk is in STANDBY mode.

If we don’t use the nocheck parameter here (when the HDD is in STANDBY mode) then the HDD will spin up and will return to ACTIVE mode. Please note if standby_mode is True then no other SMART attributes are loaded.

The most important SMART information for all disk types is the health status:

>>> if sd.healthy:
...     print("Disk is HEALTHY.")
... else:
...     print("Disk is FAILED!")
...
Disk is HEALTHY.

In case of SSDs and HDDs the traditional SMART attributes can be accessed via smart_attributes list:

>>> for item in sd.smart_attributes:
...     print(f"{item.id:>3d} {item.attribute_name}: {item.raw_value}")
...
  5 Reallocated_Sector_Ct: 0
  9 Power_On_Hours: 6356
 12 Power_Cycle_Count: 2308
177 Wear_Leveling_Count: 2
179 Used_Rsvd_Blk_Cnt_Tot: 0
181 Program_Fail_Cnt_Total: 0
182 Erase_Fail_Count_Total: 0
183 Runtime_Bad_Block: 0
187 Uncorrectable_Error_Cnt: 0
190 Airflow_Temperature_Cel: 28
195 ECC_Error_Rate: 0
199 CRC_Error_Count: 0
235 POR_Recovery_Count: 67
241 Total_LBAs_Written: 9869978356

See more details in DiskSmartData and SmartAttribute classes.

For NVMe disks, they have their own SMART data in nvme_attributes attribute:

>>> if d.is_nvme():
...     print(f"Power on hours: {sd.nvme_attributes.power_on_hours} h")
...
Power on hours: 1565 h

See the detailed list of the NVME attributes in NvmeAttributes class.

Please note that the get_smart_data() method relies on smartctl command. It means that the caller needs to have special access rights (i.e. sudo or root).

Use case 6: read partition list and file system information

After having a Disk class instance, the partition list can be read with the help of get_partition_list() method.

>>> from diskinfo import Disk
>>> d = Disk("sda")
>>> plist = d.get_partition_list()

The return value is a list of Partition classes. This class provides several get functions to access the partition attributes:

>>> from diskinfo import Disk
>>> disk = Disk("nvme0n1")
>>> plist = disk.get_partition_list()
>>> for item in plist:
...     print(item.get_name())
...
nvme0n1p1
nvme0n1p2
nvme0n1p3
nvme0n1p4
nvme0n1p5
nvme0n1p6

The Partition class contains the following partition attributes:

Attribute

Description

Sample value

name

Partition name

sda1 or nvme0n1p1

path

Partition path

/dev/sda1 or /dev/nvme0n1p1

by-id path

Persistent path in /dev/disk/by-id directory

by-path path

Persistent path in /dev/disk/by-path directory

by-partuuid path

Persistent path in /dev/disk/by-partuuid directory

by-partlabel path

Persistent path in /dev/disk/by-partlabel directory

by-uuid path

Persistent path in /dev/disk/by-uuid directory

by-label path

Persistent path in /dev/disk/by-label directory

Device ID

Partition device ID

8:1

Partition scheme

Partition scheme

gpt or mbr

Partition label

Partition label

Basic data partition

Partition UUID

Partition UUID

acb8374d-fb60-4cb0-8ac4-273417c6f847

Partition type

Partition type UUID

Partition number

Partition number in the partition table

Partition offset

Partition starting offset in 512-byte blocks

Partition size

Partition size in 512-byte blocks

File system information is stored in a separate FileSystem class. Each Partition has an associated FileSystem instance that can be accessed with the get_filesystem() method:

>>> from diskinfo import Disk
>>> disk = Disk("nvme0n1")
>>> plist = disk.get_partition_list()
>>> for item in plist:
...     fs = item.get_filesystem()
...     print(f"{item.get_name()}: {fs.get_fs_type()} mounted on {fs.get_fs_mounting_point()}")
...
nvme0n1p1: vfat mounted on /boot/efi
nvme0n1p2: ntfs mounted on
nvme0n1p3: ntfs mounted on
nvme0n1p4: ntfs mounted on
nvme0n1p5: ext4 mounted on /
nvme0n1p6: ext4 mounted on /home

The FileSystem class contains the following attributes:

Attribute

Description

Sample value

File system label

File system label

File system UUID

File system UUID

File system type

File system type

ntfs or ext4

File system version

File system version

1.0 in case of ext4

File system usage

File system usage

filesystem or other

File system free size

File system free size in 512-byte blocks

File system mounting point

File system mounting point

/ or /home

Use case 7: read raw file system information

A disk may contain a raw file system (i.e. a file system created directly on the block device without a partition table). This can be checked with has_filesystem() and the file system information can be accessed with get_filesystem():

>>> from diskinfo import Disk
>>> d = Disk("sdb")
>>> if d.has_filesystem():
...     fs = d.get_filesystem()
...     print(f"Type: {fs.get_fs_type()}, mounted on: {fs.get_fs_mounting_point()}")
... else:
...     print(f"Partition table: {d.get_partition_table_type()}")
...
Type: ext4, mounted on: /mnt/data

See FileSystem class for the full list of available file system attributes.