summaryrefslogtreecommitdiff
path: root/diskuse_ldap_linux.py
blob: 19d993c1564d8c7ae52de1286c424891f459c553 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python3
# Periodic job to update LDAP storageAvailable[/] from regular servers.
# See also storage_ldap.py for special storages.
from functools import lru_cache
import n.ldap
import os
import socket
import ssl
import sys

'''
def get_free_space(path):
    import subprocess
    result = subprocess.run(["findmnt", "-J", "-b", "--df", path],
                            stdout=subprocess.PIPE)
    result = json.loads(result.stdout)
    for fs in result["filesystems"]:
        return fs["size"], fs["size"] - fs["used"]
'''

@lru_cache()
def zpool_list():
    import subprocess
    result = subprocess.run(["zpool", "list", "-p"],
                            stdout=subprocess.PIPE)
    keys = None
    tab = {}
    for line in result.stdout.decode().splitlines():
        line = line.split()
        if not keys:
            keys = line
        else:
            row = dict(zip(keys, line))
            tab[row["NAME"]] = row
    return tab

def get_zpool_space(path):
    path = path.lstrip("/")
    pools = zpool_list()
    total_b = int(pools[path]["SIZE"])
    alloc_b = int(pools[path]["ALLOC"])
    free_b = int(pools[path]["FREE"])
    return total_b, free_b

def get_free_space(path):
    if path.startswith("ZFS/"):
        pool = path.split("/")[1]
        return get_zpool_space(pool)

    stat = os.statvfs(path)
    return (stat.f_bsize * stat.f_blocks,
            stat.f_bsize * stat.f_bfree)

def ldap_find_device_dn(ad, device):
    for dn, attrs in ad.search(ad.base,
                               n.ldap.SCOPE_SUBTREE,
                               "(&(objectClass=device)(cn=%s))",
                               [device],
                               ["1.1"]):
        return dn

def ldap_update_device(ad, device_dn, total_mb, free_mb):
    ad.modify(device_dn, replace={"storageCapacity": str(total_mb),
                                  "storageAvailable": str(free_mb)})

BYTES_PER_MB = 1024 * 1024

if os.getuid() == 0:
    n.ldap.use_system_keytab()

args = sys.argv[1:]
if args:
    volumes = [a.split("=", 1) for a in args]
else:
    volumes = [(socket.gethostname(), "/")]

ad = n.ldap.Directory()
print("Bound as", ad.whoami())

for (device, vol_path) in volumes:
    (total_bytes, free_bytes) = get_free_space(vol_path)
    total_mb = total_bytes // BYTES_PER_MB
    free_mb = free_bytes // BYTES_PER_MB
    # Reduce precision to reduce LDAP updates
    total_mb -= total_mb % 100
    free_mb -= free_mb % 100
    print("Volume '%s' at %s has ~%s MiB free out of ~%s MiB" % (device, vol_path, free_mb, total_mb))
    device_dn = ldap_find_device_dn(ad, device)
    if device_dn:
        print("Updating", device_dn)
        ldap_update_device(ad, device_dn, total_mb, free_mb)
    else:
        raise KeyError("Device %r not found in LDAP" % device)