Skip to content

Commit

Permalink
start using py-libzfs
Browse files Browse the repository at this point in the history
  • Loading branch information
name committed Aug 11, 2019
1 parent 464b03b commit 5e34efb
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 59 deletions.
2 changes: 2 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- consider SQL
- consider osquery
120 changes: 61 additions & 59 deletions zfstui/zfs.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,85 @@
#TODO generally make formatting better to fit the quality of the source material
#TODO see the entire file for todos on various assumptions that need to be fixed, this code is not reliable in edge cases (like an empty table)
import subprocess
import sys



def zfsListDatasets():
cmd = ["zfs", "list", "-r", "-o", "name,used,avail,refer,type"]
import libzfs
zfsr = libzfs.ZFS()

#A halfassed table implementation
transpose = lambda m: zip(*m)
def dict_table(obj, default_cols=None):
#https://github.com/freenas/py-libzfs/issues/63
obj = list(obj)
if len(obj) == 0:
return []
header = obj[0].properties.keys()
rows = [ [ v.value for v in o.properties.values()] for o in obj ]
#TODO ugh
_table = [header] + rows
_table = list(transpose(sorted([c for c in transpose(_table) if c[0] in default_cols], key=lambda i: i[0] != "name" ))) #sort columns; name needs to be first so interactions work right
header, rows = _table[0], _table[1:]
return table(header, rows)

##
def prop_table(obj):
return table(["NAME", "PROPERTY", "VALUE", "SOURCE"], obj ) #why is .properties a dict but .features a list generator

def prop(obj):
return [ [obj.name,k,v.value,v.source.name] for k,v in obj.properties.items() ]

def feat(obj): #TODO source column
return [ [obj.name,"feature@" + f.name,f.state.name,"-"] for f in obj.features ]

##
def table(header, rows):
table = [header] + rows
padding = " "
col_widths = [max(len(cell) + len(padding) for cell in wascolumn) for wascolumn in transpose(table)]
return ["".join((cell + padding).ljust(w) for cell,w in zip(row, col_widths)) for row in table]

##
def readCommand(cmd):
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()


def zfsListVolumes():
cmd = ["zfs", "list", "-t", "volume", "-r", "-o", "name,volsize,used,avail,refer,ratio,reserv"]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()

######
## Via API
def zfsListVolumes(): #TODO consider readding the unfiltered #TODO forgot what this means
return dict_table(filter(lambda i: i.type.name == "VOLUME", zfsr.datasets), default_cols=["name", "used", "available", "referenced", "compressratio", "quota", "reservation", "mountpoint"])

def zfsListFilesystems():
cmd = ["zfs", "list", "-t", "filesystem", "-r", "-o", "name,used,avail,refer,ratio,quota,reserv,mountpoint"]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()

return dict_table(filter(lambda i: i.type.name == "FILESYSTEM", zfsr.datasets), default_cols=["name", "used", "available", "referenced", "compressratio", "quota", "reservation", "mountpoint"])

def zfsListSnapshots():
cmd = ["zfs", "list", "-r", "-o", "name,used,creation,compressratio,referenced,written", "-t", "snap"]
stdout = subprocess.check_output(cmd, universal_newlines=True)
return stdout.splitlines()

return dict_table(zfsr.snapshots, default_cols=["name", "used", "compressratio", "referenced", "written"])

def zfsListSnapshotsOf(dataset):
cmd = ["zfs", "list", "-r", "-o", "name,creation,used,compressratio,referenced,written", "-t", "snap", dataset]
stdout = subprocess.check_output(cmd, universal_newlines=True)
return stdout.splitlines()

return dict_table(filter(lambda i: i.parent.name == dataset, zfsr.snapshots), default_cols=["name", "creation", "used", "compressratio", "referenced", "written"])

def zfsListPools():
cmd = ["zpool", "list", "-o", "name,size,alloc,free,cap,frag,dedup,health"]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()

# TODO assuming that all pools have the same set of properties, and that we have at least one pool
#TODO too wide and header doesnt scroll
return dict_table(zfsr.pools, default_cols=["name", "size", "allocated", "free", "capacity", "fragmentation", "dedupratio" , "health"]) #alloc cap frag dedup

def zfsPoolProperties(poolname):
cmd = ["zpool", "get", "all", poolname]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()


def zfsPoolHistory(poolname):
cmd = ["zpool", "history", poolname]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()


def zfsPoolIostat(poolname):
cmd = ["zpool", "iostat", "-v", poolname]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()
return prop_table(prop(next(filter(lambda i: i.name == poolname, zfsr.pools))) + feat(next(filter(lambda i: i.name == poolname, zfsr.pools))))

def zfsDatasetProperties(datasetname):
return prop_table(prop(next(filter(lambda i: i.name == datasetname, zfsr.datasets))))

def zfsSnapshotProperties(snapshotname): #TODO fix inherited to show source of inherit like in original
return prop_table(next(filter(lambda i: i.name == snapshotname, zfsr.snapshots)))

def zfsDatasetProperties(datasetname):
cmd = ["zfs", "get", "all", datasetname]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()
## Via CLI

def zfsPoolHistory(poolname): #Note needs sudo
return readCommand(["zpool", "history", poolname])

def zfsSnapshotProperties(snapshotname):
cmd = ["zfs", "get", "all", snapshotname]
stdout = subprocess.check_output(cmd, universal_newlines=True)

return stdout.splitlines()
def zfsPoolIostat(poolname): #TODO add histogram thingies
return readCommand(["zpool", "iostat", "-v", poolname])

#####

def check_zfs_executables():
try:
Expand All @@ -87,7 +89,7 @@ def check_zfs_executables():
sys.exit("zpool command not found in path")
except subprocess.CalledProcessError as e:
sys.exit(e.output)

try:
cmd = ["zfs", "list"]
stdout = subprocess.check_output(cmd, universal_newlines=True, stderr=subprocess.STDOUT)
Expand Down

0 comments on commit 5e34efb

Please sign in to comment.