Skip to content

Instantly share code, notes, and snippets.

@JonathonReinhart
Last active August 23, 2022 06:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JonathonReinhart/b89f4f93bd16fcbf9f833fdcb65955b2 to your computer and use it in GitHub Desktop.
Save JonathonReinhart/b89f4f93bd16fcbf9f833fdcb65955b2 to your computer and use it in GitHub Desktop.
Linux NBD (Network Block Device) info
#!/usr/bin/env python3
# Show Linux NBD (Network Block Device) info
# Requires Python 3.7+
# Author: Jonathon Reinhart
# License: MIT
from dataclasses import dataclass
from pathlib import Path
class NotConnected(Exception):
pass
def read_int_file(p, base=10):
return int(p.read_text(), base=base)
def human_size(s):
prefixes = ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei")
for pref in prefixes:
if s < 1024:
return f"{s} {pref}B"
s /= 1024
return f"{s} {pref}B"
def pid_comm(pid):
p = Path(f'/proc/{pid}/comm')
return p.read_text().strip()
def get_nbd_params():
paramdir = Path('/sys/module/nbd/parameters/')
results = {}
for name in ('nbds_max', 'max_part'):
results[name] = read_int_file(paramdir / name)
return results
def get_qemu_nbd_image(pid):
p = Path(f'/proc/{pid}/fd')
files = [fd for fd in p.iterdir() if fd.is_file()]
if len(files) == 1:
# NOTE: We assume that qemu-nbd only has one normal file open
return files[0].readlink()
raise Exception("forky says I dunno")
@dataclass
class NbdInfo:
pid: int
size: int
ro: bool
def infostr(self):
procdesc = self.get_procname()
try:
procdesc += " " + str(self.get_srcfile())
except (PermissionError, NotImplementedError):
pass
s = f"PID {self.pid} ({procdesc}), {human_size(self.size)}, "
s += "read-only" if self.ro else "read/write"
return s
def get_procname(self):
return pid_comm(self.pid)
def get_srcfile(self):
if self.get_procname() == "qemu-nbd":
return get_qemu_nbd_image(self.pid)
raise NotImplementedError(f"Don't know how to get srcfile for {self.get_procname()}")
def get_nbd_info(name):
p = Path('/sys/class/block') / name
try:
pid = read_int_file(p / "pid")
except FileNotFoundError:
raise NotConnected
return NbdInfo(
pid = pid,
size = read_int_file(p / "size"),
ro = bool(read_int_file(p / "ro")),
)
def main():
params = get_nbd_params()
for i in range(params['nbds_max']):
name = f'nbd{i}'
try:
info = get_nbd_info(name)
except NotConnected:
print(f"{name}: (not connected)")
continue
print(f"{name}: {info.infostr()}")
if __name__ == '__main__':
main()
@JonathonReinhart
Copy link
Author

Install into /usr/local/bin/nbdinfo.

Example output:

$ sudo nbdinfo 
nbd0: (not connected)
nbd1: PID 286233 (qemu-nbd /tmp/somedisk.vmdk), 2.0 GiB, read-only
nbd2: (not connected)
nbd3: (not connected)
nbd4: (not connected)
nbd5: (not connected)
nbd6: (not connected)
nbd7: (not connected)
nbd8: (not connected)
nbd9: (not connected)
nbd10: (not connected)
nbd11: (not connected)
nbd12: (not connected)
nbd13: (not connected)
nbd14: (not connected)
nbd15: (not connected)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment