init
This commit is contained in:
commit
f6d157e0f8
103
zfs_autobackup_explorer.py
Executable file
103
zfs_autobackup_explorer.py
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import subprocess
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ZfsProp:
|
||||||
|
prop: str
|
||||||
|
value: str
|
||||||
|
source: str
|
||||||
|
|
||||||
|
|
||||||
|
D_ZFS_PROPS = dict[str, ZfsProp]
|
||||||
|
|
||||||
|
|
||||||
|
def zfs_get_all(name: str) -> D_ZFS_PROPS:
|
||||||
|
res = dict()
|
||||||
|
s = subprocess.run(
|
||||||
|
f"zfs get -H -p -o property,value,source all {name}",
|
||||||
|
shell=True,
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
for line in s.stdout.decode().splitlines(keepends=False):
|
||||||
|
p = ZfsProp(*line.split("\t"))
|
||||||
|
res[p.prop] = p
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def no_backups_statement(props: D_ZFS_PROPS) -> str:
|
||||||
|
referenced = int(props["referenced"].value)
|
||||||
|
mounted = props["mounted"] == "yes"
|
||||||
|
mountpoint = props["mountpoint"].value
|
||||||
|
usedbysnapshots = int(props["usedbysnapshots"].value)
|
||||||
|
|
||||||
|
if (
|
||||||
|
usedbysnapshots == 0
|
||||||
|
and mounted is False
|
||||||
|
and referenced < 175_000
|
||||||
|
and mountpoint == "none"
|
||||||
|
):
|
||||||
|
return "OK; A parent dataset, no data is actually stored"
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "CHECK; Not really sure"
|
||||||
|
|
||||||
|
|
||||||
|
def main(pool_name: str, only_no_backups: bool) -> int:
|
||||||
|
p = subprocess.run(
|
||||||
|
f"zfs list -H -o name -r {pool_name}", shell=True, capture_output=True
|
||||||
|
)
|
||||||
|
if p.returncode == 1 and p.stdout == b"":
|
||||||
|
print("Pool not found")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if p.returncode != 0:
|
||||||
|
print("return code of zfs list was not 0")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
print("DATASET\tBACKUP_LABEL\tVALUE\tSOURCE\tHEURISTIC")
|
||||||
|
for ds_name in p.stdout.decode().splitlines(keepends=False):
|
||||||
|
props = zfs_get_all(ds_name)
|
||||||
|
|
||||||
|
autobackup_props: D_ZFS_PROPS = {
|
||||||
|
p: props[p] for p in props.keys() if p.startswith("autobackup:")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(autobackup_props) > 1:
|
||||||
|
raise RuntimeError(
|
||||||
|
f">1 autobackup: properties on {ds_name=!r}: {autobackup_props!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif len(autobackup_props) == 0:
|
||||||
|
msg = no_backups_statement(props)
|
||||||
|
print(f"{ds_name}\tno\t\t\t{msg}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not only_no_backups:
|
||||||
|
autobackup_prop: ZfsProp = next(iter((autobackup_props.values())))
|
||||||
|
print(
|
||||||
|
f"{ds_name}\t{autobackup_prop.prop}\t{autobackup_prop.value}\t{autobackup_prop.source}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
parser = ArgumentParser(
|
||||||
|
"zfs_autobackup_explorer",
|
||||||
|
usage="It's wise to pipe output to column tool to get "
|
||||||
|
f"formatted output: {sys.argv[0]} | column -t -s $'\\t' | less -S",
|
||||||
|
)
|
||||||
|
parser.add_argument("pool_name")
|
||||||
|
parser.add_argument("--only-no-backups", action="store_true", default=False)
|
||||||
|
args = parser.parse_args()
|
||||||
|
exit(main(args.pool_name, args.only_no_backups))
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("exiting by CTRL+C")
|
||||||
|
exit(130)
|
Loading…
x
Reference in New Issue
Block a user