#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2017, Florent Thiery ''' Criticality: Normal Checks that partitions are in conformity with expected norms, and that sufficient free space is available ''' import subprocess import sys import os BS = 512 def to_gbytes(size_bytes): return int(round(size_bytes / (1024 * 1024 * 1024))) def read_file(fname): with open(fname, 'r') as f: return f.read() paths = [ { 'mount_point': '/', 'recommended_types': ('ext4',), 'min_size_gbytes': 14, 'min_free_size_gbytes': 5, }, { 'mount_point': '/home/msuser/msinstance', 'recommended_types': ('ext4', 'nfsv4'), 'min_size_gbytes': 400, 'min_free_size_gbytes': 10, }, { 'type': 'swap', 'min_size_gbytes': 1, }, ] def get_swap_gbytes(): d = read_file('/proc/meminfo') for l in d.split('\n'): if 'SwapTotal'in l: swap = l.split('SwapTotal:')[1].strip() swap_kbytes, unit = swap.split(' ') if unit != 'kB': print('Warning, unexpected unit %s' % unit) swap_gbytes = int(round(int(swap_kbytes) / (1024 * 1024))) return swap_gbytes def get_path(path): # Filesystem Type 1B-blocks Used Available Use% Mounted on # /dev/loop2 ext4 52710469632 38253940736 11755397120 77% / status, output = subprocess.getstatusoutput('df --output="source,fstype,size,used" -B 1 %s | tail -n 1' % path) if status == 0: dev, fstype, size, used = output.split() else: dev = fstype = size = used = None return dev, fstype, to_gbytes(int(size)), to_gbytes(int(used)) def check_allocation(dev): root_dev = os.path.basename(dev)[:3] d = read_file('/proc/partitions') dev_partitions = list() for l in d.split('\n'): if root_dev in l: dev_partitions.append(l) max_size = 0 total_size = 0 for p in dev_partitions: major, minor, blocks, name = p.split() size = int(blocks) * 512 if name == root_dev: max_size = size else: total_size += size unallocated = max_size - total_size unallocated_gbytes = to_gbytes(unallocated) if unallocated_gbytes > 1: print('Warning, %s GB are unallocated on %s' % (unallocated_gbytes, root_dev)) return False return True error = False warning = False def check_path(p): global error global warning psize = None mount_point = p.get('mount_point') if mount_point: if os.path.isdir(mount_point): name = mount_point dev, fstype, psize, used = get_path(p['mount_point']) if fstype not in p.get('recommended_types'): print('Warning, fs type %s not recommended (recommended: %s)' % (fstype, p['recommended_types'])) warning = True if 'nfs' not in dev: warning = not check_allocation(dev) free_gb = psize - used min_free_size_gbytes = p.get('min_free_size_gbytes') if min_free_size_gbytes and free_gb < min_free_size_gbytes: print('%s has less than %s GB free (%s GB free)' % (mount_point, min_free_size_gbytes, free_gb)) error = True else: print('%s not found, cannot check' % mount_point) elif p.get('type') == 'swap': name = 'swap' psize = get_swap_gbytes() if psize and psize < p['min_size_gbytes']: print('%s is smaller than the minimum required size (%s GB < %s GB)' % (name, psize, p['min_size_gbytes'])) error = True for p in paths: check_path(p) OK = 0 ERROR = 1 WARNING = 3 UNTESTABLE = 2 if error: print('Errors found') code = ERROR elif warning: print('Some warnings were found') code = WARNING else: print('All ok') code = OK sys.exit(code)