Last active
December 2, 2019 00:10
-
-
Save zanglang/0ab8113f9682c4a99b63cdcc5ec11f2f to your computer and use it in GitHub Desktop.
Compares NFSv4 mounts defined in /etc/fstab and current mounts on the system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Usage: check-nfs-mounts-v2.py | |
Compares NFSv4 mounts defined in /etc/fstab and current mounts on the system | |
""" | |
from __future__ import print_function | |
import re | |
try: | |
import subprocess32 as subprocess | |
except ImportError: | |
import subprocess | |
from collections import Counter | |
from copy import copy | |
def log(output_str, *args): | |
"""For debug logging""" | |
if False: # if True: | |
print(output_str.format(*args)) | |
def check_nfs_mounts(expected, current): | |
"""Compare the list of expected NFS mounts against current mounts""" | |
# prepare to store error messages | |
errors = [] | |
# quick check to see if number of mounts are correct | |
log("Expecting to have {} mounts", len(expected)) | |
log("Found {} current mounts", len(current)) | |
if len(expected) != len(current): | |
errors.append(Warning("Expected %d mounts, got %d!\n" % | |
(len(expected), len(current)))) | |
# quick check to find duplicate mounts | |
counter = Counter(mount_point for server, path, mount_point in current) | |
for mount_point, count in list(counter.items()): | |
if count > 1: | |
# expected mount is not found | |
errors.append(Warning("Duplicate mount: %s!\n" % mount_point)) | |
# clone the list of current mounts line. we'll pop correctly matched | |
# mounts from the buffer as they are found - any leftovers are presume | |
# incorrect. | |
buffer = copy(current) | |
# compare the current mounts with the expected mounts in /etc/fstab | |
for mount in expected: | |
log("Checking: {}:{} on {}", mount[0], mount[1], mount[2]) | |
for current_mount in current: | |
if current_mount[0] == mount[0] and \ | |
current_mount[1] == mount[1] and \ | |
current_mount[2] == mount[2]: | |
log("Found.") | |
buffer.remove(current_mount) | |
break | |
else: | |
# expected mount is not found | |
log("Couldn't find {}:{} on {}", mount[0], mount[1], mount[2]) | |
errors.append(Warning("Missing mount: %s:%s on %s!\n" % | |
(mount[0], mount[1], mount[2]))) | |
# if `buffer` is not empty, some mounts are incorrectly mounted | |
for current_server, current_path, current_mount in buffer: | |
errors.append(Exception("Incorrect extra mounts: %s:%s on %s!\n" % | |
(current_server, current_path, current_mount))) | |
return errors | |
def parse_fstab(): | |
"""Parse /etc/fstab for a list of expected NFS mounts""" | |
pattern = re.compile( | |
r"(\w+\d+)\:(\/\w+) (\/nfs[\w\/]+\d+) nfs ([\w\d=,]+) ") | |
try: | |
with open("/etc/fstab") as fstab: | |
return pattern.findall(fstab.read()) | |
except: | |
# unable to read /etc/fstab for some reason! we should immediately | |
# return at this point and avoid further processing. | |
print("UNKNOWN - could not open /etc/fstab!") | |
exit(3) | |
def parse_mounts(): | |
"""Parse the output of `df` for a list of current NFS mounts""" | |
try: | |
stdout = subprocess.check_output( | |
"df -t nfs4 -h -a | grep -E -v \"(primary|secondary)\" | sort", | |
stderr=subprocess.STDOUT, shell=True, timeout=60) # 60 seconds | |
return re.findall( | |
r"(\w+\d+)\:(\/\w+)\s+.*?(\/nfs[\w\/]+\d+)", stdout) | |
except: | |
# unable to run df for some reason! we should immediately return at | |
# this point and avoid further processing. | |
import traceback | |
print("UNKNOWN - couldn't run NFS mount check!\n", | |
traceback.format_exc()) | |
exit(3) | |
if __name__ == '__main__': | |
EXPECTED = parse_fstab() | |
CURRENT = parse_mounts() | |
RESULTS = check_nfs_mounts(EXPECTED, CURRENT) | |
# process and print returned results | |
if not len(RESULTS): | |
return_code = 0 | |
print("OK - ", end='') | |
elif any(type(result) == Exception for result in RESULTS): | |
return_code = 2 | |
print("CRITICAL - NFS mount check failure: ", end='') | |
else: | |
return_code = 1 | |
print("WARNING - NFS mount check warning: ", end='') | |
print("%s/%s\n" % (len(CURRENT), len(EXPECTED))) | |
for result in RESULTS: | |
print(type(result) == Exception and "E: " or "W: ", | |
result.message.strip()) | |
exit(return_code) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment