Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pylsblk to introspect block devices #24

Merged
merged 2 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions bin/pylsblk
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3

import os
import yaml

def get_device_info(device_name):
info = {
'device': device_name,
'sector_size': 512,
'size': 0,
'mount': None,
'model': None,
'tran': 'sata',
'rota': True,
'parent': None
}

sector_size_file = f'/sys/class/block/{device_name}/queue/hw_sector_size'
if os.path.exists(sector_size_file):
with open(sector_size_file) as sector_size_fd:
info['sector_size'] = int(sector_size_fd.read().strip()) * 512

# Get device size
size_file = f'/sys/class/block/{device_name}/size'
if os.path.exists(size_file):
with open(size_file) as size_fd:
info['size'] = int(size_fd.read().strip()) * info['sector_size']

# Get mount point
with open('/proc/mounts') as mounts_fd:
for line in mounts_fd:
parts = line.strip().split()
if len(parts) >= 2 and parts[0] == f'/dev/{device_name}':
info['mount'] = parts[1]

# Get device model
model_file = f'/sys/class/block/{device_name}/device/model'
if os.path.exists(model_file):
with open(model_file) as model_fd:
info['model'] = model_fd.read().strip()

# Get device transport
transport_file = f'/sys/class/block/{device_name}/device/transport'
if os.path.exists(transport_file):
with open(transport_file) as transport_fd:
info['tran'] = transport_fd.read().strip()

# Get device rotation
rotational_file = f'/sys/class/block/{device_name}/queue/rotational'
if os.path.exists(rotational_file):
with open(rotational_file) as rotational_fd:
info['rota'] = int(rotational_fd.read().strip())

return info


def main():
bdev_names = [bdev_name for bdev_name in os.listdir('/sys/class/block/')
if not bdev_name.startswith('loop')]
bdevs = [get_device_info(bdev_name)
for bdev_name in bdev_names]

# Get correct lineage
for parent in bdevs:
for child in bdevs:
if parent['device'] in child['device'] and \
parent['device'] != child['device']:
child['parent'] = parent['device']
child['model'] = parent['model']
child['rota'] = parent['rota']
child['tran'] = parent['tran']

# Print bdev info
print(yaml.dump(bdevs))

if __name__ == '__main__':
main()
45 changes: 42 additions & 3 deletions jarvis_util/introspect/system_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from jarvis_util.serialize.yaml_file import YamlFile
import jarvis_util.util.small_df as sdf
import json
import yaml
import shlex
import ipaddress
import copy
Expand Down Expand Up @@ -155,14 +156,52 @@ def wait(self):
print(self.df)


class PyLsblk(Exec):
"""
List all block devices in the system per-node. PyLsblk will return
a YAML output

A table is stored per-host:
parent: the parent device of the partition (e.g., /dev/sda or NaN)
device: the name of the partition (e.g., /dev/sda1)
size: total size of the partition (bytes)
mount: where the partition is mounted (if anywhere)
model: the exact model of the device
tran: the transport of the device (e.g., /dev/nvme)
rota: whether or not the device is rotational
host: the host this record corresponds to
"""

def __init__(self, exec_info):
cmd = 'pylsblk'
super().__init__(cmd, exec_info.mod(collect_output=True))
self.exec_async = exec_info.exec_async
self.df = None
if not self.exec_async:
self.wait()

def wait(self):
super().wait()
total = []
for host, stdout in self.stdout.items():
lsblk_data = yaml.load(stdout, Loader=yaml.FullLoader)
for dev in lsblk_data:
if dev['tran'] == 'pcie':
dev['tran'] = 'nvme'
dev['host'] = host
total.append(dev)
self.df = sdf.SmallDf(rows=total)
print(self.df)


class Blkid(Exec):
"""
List all filesystems (even those unmounted) and their properties

Stores a per-host table with the following:
device: the device (or partition) which stores the data (e.g., /dev/sda)
fs_type: the type of filesystem (e.g., ext4)
uuid: filesystem-levle uuid from the FS metadata
uuid: filesystem-level uuid from the FS metadata
partuuid: the partition-lable UUID for the partition
partlabel: semantic partition type information
label: semantic label given by users
Expand Down Expand Up @@ -341,7 +380,7 @@ def build(self, exec_info, introspect=True):
Build a resource graph.

:param exec_info: Where to collect resource information
:param introspect: Whether to introspect system info, or rely solely
:param introspect: Whether to pylsblk system info, or rely solely
on admin-defined settings
:return: self
"""
Expand Down Expand Up @@ -542,7 +581,7 @@ def _introspect(self, exec_info):
:param exec_info: Where to collect resource information
:return: None
"""
self.lsblk = Lsblk(exec_info.mod(hide_output=True))
self.lsblk = PyLsblk(exec_info.mod(hide_output=True))
self.blkid = Blkid(exec_info.mod(hide_output=True))
self.list_fs = ListFses(exec_info.mod(hide_output=True))
self.fi_info = FiInfo(exec_info.mod(hide_output=True))
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
setuptools.setup(
name="jarvis_util",
packages=setuptools.find_packages(),
scripts=['bin/pylsblk'],
version="0.0.1",
author="Luke Logan",
author_email="[email protected]",
Expand Down
Loading