#!/bin/bash # NFS Performance Validation Script # Validates NFS performance and connectivity across the infrastructure set -euo pipefail # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to print colored output print_status() { echo -e "${GREEN}[INFO]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_header() { echo -e "${BLUE}[HEADER]${NC} $1" } # Configuration NFS_SERVER="omv800.local" NFS_EXPORT="/export" TEST_DIR="/mnt/nfs_test" TEST_FILE_SIZE="100M" TEST_ITERATIONS=5 HOSTS=("omv800.local" "jonathan-2518f5u" "surface" "fedora" "audrey") OUTPUT_FILE="${1:-/tmp/nfs_performance_report.txt}" # Function to check if NFS server is accessible check_nfs_server() { print_header "Checking NFS Server Accessibility" if ! ping -c 1 "$NFS_SERVER" >/dev/null 2>&1; then print_error "NFS server $NFS_SERVER is not reachable" return 1 fi if ! showmount -e "$NFS_SERVER" >/dev/null 2>&1; then print_error "Cannot get exports from $NFS_SERVER" return 1 fi print_status "NFS server $NFS_SERVER is accessible" showmount -e "$NFS_SERVER" } # Function to mount NFS export mount_nfs_export() { print_header "Mounting NFS Export" # Create test directory sudo mkdir -p "$TEST_DIR" # Unmount if already mounted if mountpoint -q "$TEST_DIR"; then print_status "Unmounting existing mount at $TEST_DIR" sudo umount "$TEST_DIR" fi # Mount NFS export if sudo mount -t nfs "$NFS_SERVER:$NFS_EXPORT" "$TEST_DIR"; then print_status "Successfully mounted $NFS_SERVER:$NFS_EXPORT to $TEST_DIR" return 0 else print_error "Failed to mount $NFS_SERVER:$NFS_EXPORT" return 1 fi } # Function to test write performance test_write_performance() { print_header "Testing Write Performance" local test_file="$TEST_DIR/write_test_$(date +%s)" local total_time=0 local total_size=0 for i in $(seq 1 $TEST_ITERATIONS); do print_status "Write test iteration $i/$TEST_ITERATIONS" local start_time=$(date +%s.%N) if dd if=/dev/zero of="$test_file.$i" bs=1M count=100 2>/dev/null; then local end_time=$(date +%s.%N) local duration=$(echo "$end_time - $start_time" | bc -l) local size=$(stat -c%s "$test_file.$i" 2>/dev/null || echo "0") total_time=$(echo "$total_time + $duration" | bc -l) total_size=$(echo "$total_size + $size" | bc -l) print_status "Iteration $i: ${size} bytes in ${duration}s" else print_error "Write test iteration $i failed" fi done local avg_time=$(echo "$total_time / $TEST_ITERATIONS" | bc -l) local avg_size=$(echo "$total_size / $TEST_ITERATIONS" | bc -l) local write_speed=$(echo "$avg_size / $avg_time / 1024 / 1024" | bc -l) echo "Write Performance: ${write_speed} MB/s average" >> "$OUTPUT_FILE" print_status "Write Performance: ${write_speed} MB/s average" } # Function to test read performance test_read_performance() { print_header "Testing Read Performance" local test_file="$TEST_DIR/read_test_$(date +%s)" local total_time=0 local total_size=0 # Create a test file first dd if=/dev/zero of="$test_file" bs=1M count=100 2>/dev/null for i in $(seq 1 $TEST_ITERATIONS); do print_status "Read test iteration $i/$TEST_ITERATIONS" local start_time=$(date +%s.%N) if dd if="$test_file" of=/dev/null bs=1M 2>/dev/null; then local end_time=$(date +%s.%N) local duration=$(echo "$end_time - $start_time" | bc -l) local size=$(stat -c%s "$test_file" 2>/dev/null || echo "0") total_time=$(echo "$total_time + $duration" | bc -l) total_size=$(echo "$total_size + $size" | bc -l) print_status "Iteration $i: ${size} bytes in ${duration}s" else print_error "Read test iteration $i failed" fi done local avg_time=$(echo "$total_time / $TEST_ITERATIONS" | bc -l) local avg_size=$(echo "$total_size / $TEST_ITERATIONS" | bc -l) local read_speed=$(echo "$avg_size / $avg_time / 1024 / 1024" | bc -l) echo "Read Performance: ${read_speed} MB/s average" >> "$OUTPUT_FILE" print_status "Read Performance: ${read_speed} MB/s average" # Cleanup test file rm -f "$test_file" } # Function to test concurrent access test_concurrent_access() { print_header "Testing Concurrent Access" local test_file="$TEST_DIR/concurrent_test" local num_processes=10 local test_duration=30 # Create test file dd if=/dev/zero of="$test_file" bs=1M count=10 2>/dev/null print_status "Starting $num_processes concurrent processes for ${test_duration}s" local start_time=$(date +%s) # Start concurrent processes for i in $(seq 1 $num_processes); do ( while [ $(($(date +%s) - start_time)) -lt $test_duration ]; do dd if="$test_file" of=/dev/null bs=1M count=1 2>/dev/null sleep 0.1 done ) & done # Wait for all processes wait local end_time=$(date +%s) local total_time=$((end_time - start_time)) echo "Concurrent Access: $num_processes processes for ${total_time}s - PASSED" >> "$OUTPUT_FILE" print_status "Concurrent access test completed successfully" # Cleanup rm -f "$test_file" } # Function to test network latency test_network_latency() { print_header "Testing Network Latency" local total_latency=0 local ping_count=10 for i in $(seq 1 $ping_count); do local latency=$(ping -c 1 "$NFS_SERVER" 2>/dev/null | grep "time=" | cut -d'=' -f4 | cut -d' ' -f1) if [ -n "$latency" ]; then total_latency=$(echo "$total_latency + $latency" | bc -l) fi done local avg_latency=$(echo "$total_latency / $ping_count" | bc -l) echo "Network Latency: ${avg_latency}ms average" >> "$OUTPUT_FILE" print_status "Network Latency: ${avg_latency}ms average" } # Function to check NFS mount options check_mount_options() { print_header "Checking NFS Mount Options" if mountpoint -q "$TEST_DIR"; then local mount_info=$(mount | grep "$TEST_DIR") echo "Mount Options: $mount_info" >> "$OUTPUT_FILE" print_status "Current mount: $mount_info" # Check for performance options if echo "$mount_info" | grep -q "rsize="; then print_status "Read buffer size configured" else print_warning "Read buffer size not configured (consider rsize=32768)" fi if echo "$mount_info" | grep -q "wsize="; then print_status "Write buffer size configured" else print_warning "Write buffer size not configured (consider wsize=32768)" fi if echo "$mount_info" | grep -q "noatime"; then print_status "No access time updates configured" else print_warning "Access time updates enabled (consider noatime for performance)" fi else print_error "NFS not mounted at $TEST_DIR" fi } # Function to generate performance report generate_report() { print_header "Generating Performance Report" echo "=== NFS Performance Validation Report ===" > "$OUTPUT_FILE" echo "Date: $(date)" >> "$OUTPUT_FILE" echo "NFS Server: $NFS_SERVER" >> "$OUTPUT_FILE" echo "NFS Export: $NFS_EXPORT" >> "$OUTPUT_FILE" echo "Test Directory: $TEST_DIR" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE" # Add system information echo "=== System Information ===" >> "$OUTPUT_FILE" echo "Hostname: $(hostname)" >> "$OUTPUT_FILE" echo "Kernel: $(uname -r)" >> "$OUTPUT_FILE" echo "NFS Client Version: $(nfsstat -c | head -1)" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE" # Add network information echo "=== Network Information ===" >> "$OUTPUT_FILE" ip route get "$NFS_SERVER" >> "$OUTPUT_FILE" 2>/dev/null || echo "Cannot determine route to $NFS_SERVER" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE" print_status "Performance report saved to $OUTPUT_FILE" } # Function to cleanup cleanup() { print_header "Cleaning Up" # Remove test files rm -f "$TEST_DIR"/test_* 2>/dev/null || true # Unmount NFS if mountpoint -q "$TEST_DIR"; then sudo umount "$TEST_DIR" print_status "Unmounted $TEST_DIR" fi # Remove test directory sudo rmdir "$TEST_DIR" 2>/dev/null || true } # Function to display usage usage() { echo "Usage: $0 [output_file]" echo " output_file: Path to save performance report (default: /tmp/nfs_performance_report.txt)" echo "" echo "This script validates NFS performance and connectivity." echo "It performs write/read tests, concurrent access tests, and network latency tests." } # Main execution main() { print_header "Starting NFS Performance Validation" # Check if running as root (needed for mounting) if [ "$EUID" -ne 0 ]; then print_error "This script must be run as root (needed for NFS mounting)" exit 1 fi # Check dependencies for cmd in ping showmount mount dd bc nfsstat; do if ! command -v "$cmd" >/dev/null 2>&1; then print_error "Required command '$cmd' not found" exit 1 fi done # Initialize report generate_report # Run tests if check_nfs_server && mount_nfs_export; then test_network_latency check_mount_options test_write_performance test_read_performance test_concurrent_access print_status "All NFS performance tests completed successfully" print_status "Report saved to: $OUTPUT_FILE" else print_error "NFS validation failed - cannot proceed with performance tests" exit 1 fi } # Trap to ensure cleanup on exit trap cleanup EXIT # Parse command line arguments if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then usage exit 0 fi # Run main function main "$@"