Linux Configuration

Verifying Linux Setting in Python

The following Python script checks several Linux settings. Results of the check are written to the console. If the results are within the desired ranges and values, the results are displayed in green. If the values are outside of the desired ragnes, results are displayed in red.

All results are written to a json outfile located at /tmp/linuxverification.json

Python code

import subprocess
import socket
import os
import json
import platform

def get_value_from_sys_path(path):
    try:
        with open(path, 'r') as f:
            return f.read().strip()
    except Exception:
        return None

# Check function for exact values
def check_value_exact(value, target):
    return str(value) == str(target)

# Check function for in_range column
# If the value is in range, then return True
def check_value_in_range(value, check_func):
    if value is None:
        return False
    return check_func(value)

# Get system total memory
def get_memory_info():
    with open("/proc/meminfo", "r") as f:
        for line in f.readlines():
            if "MemTotal" in line:
                memory_kb = int(line.split()[1])
                return memory_kb / (1024 * 1024)  # Convert KB to GB

# Checks the Transparent Huge Pages status
# The status can be one of the following:
# always, madvise, never
def check_thp_status():
    path = "/sys/kernel/mm/transparent_hugepage/enabled"
    thp_value = get_value_from_sys_path(path)
    if "[never]" in thp_value:
        return (True, "[never]")
    elif "[always]" in thp_value:
        return (False, "[always]")
    elif "[madvise]" in thp_value:
        return (False, "[madvise]")
    else:
        return (None, "Unknown")

# Get network adapters and their IP addresses
def get_ip_addresses():
    try:
        output = subprocess.check_output(["hostname", "--all-ip-addresses"]).decode('utf-8').strip()
        return output.split()
    except Exception as e:
        print(f"Error fetching IP addresses: {e}")
        return []

# Print colored text to console based acceptable ranges
def print_colored(text, color):
    endc = '\033[0m'
    print(f"{color}{text}{endc}")

def main():
    settings = {
        "vm.swappiness": {
            "path": "/proc/sys/vm/swappiness",
            "acceptable": "10 or less",
            "check_func": lambda x: int(x) <= 10
        },
        "vm.dirty_ratio": {
            "path": "/proc/sys/vm/dirty_ratio",
            "acceptable": "less than 15",
            "check_func": lambda x: int(x) < 15
        },
        "vm.dirty_background_ratio": {
            "path": "/proc/sys/vm/dirty_background_ratio",
            "acceptable": "5 or less",
            "check_func": lambda x: int(x) <= 5
        },
        "net.core.somaxconn": {
            "path": "/proc/sys/net/core/somaxconn",
            "acceptable": "4096",
            "check_func": lambda x: check_value_exact(x, 4096)
        },
        'net.ipv4.tcp_fin_timeout': {
            'path': '/proc/sys/net/ipv4/tcp_fin_timeout',
            'acceptable': 30,
            'check_func': lambda x: check_value_exact(x, 30)
        },
        'net.ipv4.tcp_keepalive_intvl': {
            'path': "/proc/sys/net/ipv4/tcp_keepalive_intvl",
            'acceptable': 30,
            'check_func': lambda x: check_value_exact(x, 30)
        },
        'net.ipv4.tcp_keepalive_time': {
            'path': "/proc/sys/net/ipv4/tcp_keepalive_time",
            'acceptable': 120,
            'check_func': lambda x: check_value_exact(x, 120)
        },
        'net.ipv4.tcp_max_syn_backlog': {
            'path': "/proc/sys/net/ipv4/tcp_max_syn_backlog",
            'acceptable': 4096,
            'check_func': lambda x: check_value_exact(x, 4096)
        },
        'net.ipv4.tcp_keepalive_probes': {
            'path': "/proc/sys/net/ipv4/tcp_keepalive_probes",
            'acceptable': 6,
            'check_func': lambda x: check_value_exact(x, 6)
        }
    }

    # Add the Server specs to the results dictionary
    # which will be used in the output file.
    results = {
        "Server Name": socket.gethostname(),
        "Processor Information": platform.processor(),
        "Number of Processor Cores": os.cpu_count(),
        "Total Memory (GB)": get_memory_info(),
        "IP Addresses": get_ip_addresses()
    }

    # Display the server specs on the terminal
    print(f"Server Name: {results['Server Name']}")
    print(f"Processor Information: {results['Processor Information']}")
    print(f"Number of Processor Cores: {results['Number of Processor Cores']}")
    print(f"Total Memory (GB): {results['Total Memory (GB)']:.2f}")
    for ip in results['IP Addresses']:
        print(f"IP Address: {ip}")
    print("-" * 91)

    # Print settings table header
    print("{:<30} {:<30} {:<20} {:<10}".format("Setting", "Acceptable Ranges", "Current Value", "In Range"))
    print("-" * 91)

    # Build the output dictionary
    output = {}

    # Loop over the settings dictionary
    # Determine the current value of the setting and if the
    # value is in the acceptable range. If so, then print
    # the setting in green, otherwise print it in red.
    # We also add the setting to the output dictionary
    for setting_name, setting_details in settings.items():
        value = get_value_from_sys_path(setting_details["path"])
        in_range = check_value_in_range(value, setting_details["check_func"])
        
        print_colored(
            "{:<30} {:<30} {:<20} {:<10}".format(
                setting_name, 
                str(setting_details["acceptable"]), 
                str(value),
                str(in_range)
            ),
            "\033[92m" if in_range else "\033[91m"
        )

        # Add the setting to the output dictionary
        # which will be used in the out file
        output[setting_name] = {
            "current_value": value,
            "acceptable_values": setting_details["acceptable"],
            "in_range": in_range
        }

    # Add the settings to the results dictionary
    results["Settings"] = output

    # Save results to a JSON file
    with open("/tmp/linuxverification.json", "w") as file:
        json.dump(results, file, indent=4)

if __name__ == "__main__":
    main()

By Rudy