13d7054aa1
- Fix line 63 in php-analyzer.sh: Add default value for count variable (integer comparison error) - Fix line 655 in php-analyzer.sh: Add default value for memory_error_count (integer comparison error) - Fix line 396 in php-scanner.sh: Replace unsafe eval with safe getent passwd lookup - Add php-ui.sh: User interface and menu system (18KB, 25+ functions) - Add php-scanner.sh: Server enumeration system (17KB, 18 functions) - Add php-action-executor.sh: Optimization execution system (17KB, 20 functions) - Add php-server-manager.sh: Orchestration framework (21KB, 7 functions) - Add php-fpm-batch-analyzer.sh: One-shot diagnostic script showing current vs recommended max_children, memory impact, and optimization potential - Add comprehensive test suite (24 tests) These fixes resolve "integer expression expected" errors during domain analysis. Batch analyzer enables users to see domain-by-domain optimization opportunities before applying changes. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
542 lines
21 KiB
Bash
Executable File
542 lines
21 KiB
Bash
Executable File
#!/bin/bash
|
|
# PHP-FPM Server Manager Module
|
|
# Orchestrates large-scale server operations: scanning, planning, executing, reporting
|
|
# Part of PHP Optimizer - Phase 3 Refactoring
|
|
|
|
# ============================================================================
|
|
# SERVER SCANNING & INVENTORY
|
|
# ============================================================================
|
|
|
|
# Scan entire server and collect comprehensive information
|
|
scan_entire_server() {
|
|
local filter_mode="${1:-all}" # all, user, pattern, traffic, needs_optimization
|
|
local filter_arg="${2:-}"
|
|
|
|
init_change_tracking
|
|
|
|
local -a domains_to_analyze
|
|
|
|
case "$filter_mode" in
|
|
all)
|
|
mapfile -t domains_to_analyze < <(enumerate_all_domains)
|
|
;;
|
|
user)
|
|
[ -z "$filter_arg" ] && return 1
|
|
mapfile -t domains_to_analyze < <(enumerate_user_domains "$filter_arg")
|
|
;;
|
|
pattern)
|
|
[ -z "$filter_arg" ] && return 1
|
|
mapfile -t domains_to_analyze < <(filter_domains_by_name "$filter_arg")
|
|
;;
|
|
traffic)
|
|
[ -z "$filter_arg" ] && filter_arg="100"
|
|
mapfile -t domains_to_analyze < <(filter_domains_by_traffic "$filter_arg" "above")
|
|
;;
|
|
needs_optimization)
|
|
mapfile -t domains_to_analyze < <(filter_domains_by_optimization_status "needs_optimization")
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
local total_domains=${#domains_to_analyze[@]}
|
|
local current=0
|
|
local -A scan_results
|
|
|
|
if [ "$total_domains" -eq 0 ]; then
|
|
return 0
|
|
fi
|
|
|
|
for domain in "${domains_to_analyze[@]}"; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
current=$((current + 1))
|
|
show_enumeration_progress "$current" "$total_domains"
|
|
|
|
# Collect domain info
|
|
local owner
|
|
owner=$(find_domain_owner "$domain")
|
|
|
|
local issues
|
|
issues=$(detect_php_config_issues "$owner" "$domain" 2>/dev/null || echo "")
|
|
|
|
local issue_count
|
|
issue_count=$(echo "$issues" | grep -c "^" || echo "0")
|
|
|
|
scan_results["$domain"]="$owner|$issue_count|$issues"
|
|
done
|
|
|
|
echo ""
|
|
|
|
# Output results in scannable format
|
|
for domain in "${!scan_results[@]}"; do
|
|
echo "DOMAIN|$domain|${scan_results[$domain]}"
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
# Analyze entire server for optimization opportunities
|
|
analyze_entire_server() {
|
|
local -a all_domains
|
|
|
|
mapfile -t all_domains < <(enumerate_all_domains)
|
|
|
|
local total_domains=${#all_domains[@]}
|
|
local domains_with_issues=0
|
|
local critical_count=0
|
|
local high_count=0
|
|
local medium_count=0
|
|
local low_count=0
|
|
|
|
local current=0
|
|
|
|
for domain in "${all_domains[@]}"; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
current=$((current + 1))
|
|
display_progress "$current" "$total_domains" "Analyzing"
|
|
|
|
local owner
|
|
owner=$(find_domain_owner "$domain")
|
|
|
|
if [ -z "$owner" ]; then
|
|
continue
|
|
fi
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$owner" "$domain" 2>/dev/null)
|
|
|
|
# Count issues by severity
|
|
local c_count h_count m_count l_count
|
|
c_count=$(echo "$issues" | grep -c "^[^|]*|CRITICAL|" || echo "0")
|
|
h_count=$(echo "$issues" | grep -c "^[^|]*|HIGH|" || echo "0")
|
|
m_count=$(echo "$issues" | grep -c "^[^|]*|MEDIUM|" || echo "0")
|
|
l_count=$(echo "$issues" | grep -c "^[^|]*|LOW|" || echo "0")
|
|
|
|
if [ $((c_count + h_count + m_count + l_count)) -gt 0 ]; then
|
|
domains_with_issues=$((domains_with_issues + 1))
|
|
critical_count=$((critical_count + c_count))
|
|
high_count=$((high_count + h_count))
|
|
medium_count=$((medium_count + m_count))
|
|
low_count=$((low_count + l_count))
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "$total_domains|$domains_with_issues|$critical_count|$high_count|$medium_count|$low_count"
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION PLANNING
|
|
# ============================================================================
|
|
|
|
# Plan optimizations for entire server
|
|
plan_server_optimizations() {
|
|
local filter_mode="${1:-needs_optimization}"
|
|
local filter_arg="${2:-}"
|
|
local dry_run="${3:-true}"
|
|
|
|
local -a domains_to_optimize
|
|
mapfile -t domains_to_optimize < <(scan_entire_server "$filter_mode" "$filter_arg")
|
|
|
|
local total_domains=0
|
|
local optimization_count=0
|
|
|
|
# Parse scan results and identify optimization opportunities
|
|
declare -A optimization_plan
|
|
|
|
while IFS='|' read -r type domain owner issue_count rest; do
|
|
[ "$type" != "DOMAIN" ] && continue
|
|
[ -z "$domain" ] && continue
|
|
|
|
total_domains=$((total_domains + 1))
|
|
|
|
if [ "$issue_count" -gt 0 ]; then
|
|
optimization_count=$((optimization_count + 1))
|
|
optimization_plan["$domain"]="$owner|$issue_count"
|
|
fi
|
|
done <<< "$(echo "${domains_to_optimize[@]}" | tr ' ' '\n')"
|
|
|
|
# Generate plan summary
|
|
echo "OPTIMIZATION_PLAN"
|
|
echo "Total domains: $total_domains"
|
|
echo "Domains needing optimization: $optimization_count"
|
|
echo ""
|
|
|
|
# List domains to be optimized
|
|
for domain in "${!optimization_plan[@]}"; do
|
|
local owner issue_count
|
|
owner=$(echo "${optimization_plan[$domain]}" | cut -d'|' -f1)
|
|
issue_count=$(echo "${optimization_plan[$domain]}" | cut -d'|' -f2)
|
|
echo " - $domain (owner: $owner, $issue_count issues)"
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION EXECUTION
|
|
# ============================================================================
|
|
|
|
# Execute planned optimizations across server
|
|
execute_server_optimization_plan() {
|
|
local -a domains=("$@")
|
|
local dry_run="${DRY_RUN:-false}"
|
|
local require_confirmation="${REQUIRE_CONFIRMATION:-true}"
|
|
|
|
if [ ${#domains[@]} -eq 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
# Show summary before executing
|
|
local total=${#domains[@]}
|
|
echo ""
|
|
echo "Server Optimization Summary:"
|
|
echo " Total domains to optimize: $total"
|
|
echo " Dry-run mode: $dry_run"
|
|
echo ""
|
|
|
|
if [ "$require_confirmation" = "true" ]; then
|
|
if ! confirm "Execute optimizations for $total domain(s)?"; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
init_change_tracking
|
|
|
|
local successful=0
|
|
local failed=0
|
|
local current=0
|
|
|
|
for domain in "${domains[@]}"; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
current=$((current + 1))
|
|
display_progress "$current" "$total" "Optimizing"
|
|
|
|
local owner
|
|
owner=$(find_domain_owner "$domain")
|
|
|
|
if [ -z "$owner" ]; then
|
|
failed=$((failed + 1))
|
|
log_change "$domain" "server_optimization" "unknown_owner" "skipped" "failed"
|
|
continue
|
|
fi
|
|
|
|
# Apply optimizations
|
|
if apply_optimization "$domain" "$owner" "all" "$dry_run"; then
|
|
successful=$((successful + 1))
|
|
else
|
|
failed=$((failed + 1))
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "Optimization Results:"
|
|
echo " Successful: $successful"
|
|
echo " Failed: $failed"
|
|
echo " Total: $((successful + failed))"
|
|
|
|
# Reload PHP-FPM once for all changes
|
|
if [ "$dry_run" != "true" ] && [ "$successful" -gt 0 ]; then
|
|
echo "Reloading PHP-FPM to apply changes..."
|
|
reload_php_fpm
|
|
fi
|
|
|
|
return $((failed > 0 ? 1 : 0))
|
|
}
|
|
|
|
# ============================================================================
|
|
# REPORTING
|
|
# ============================================================================
|
|
|
|
# Generate comprehensive server analysis report
|
|
generate_server_report() {
|
|
local report_file="${1:-/tmp/php-optimizer-server-report-$(date +%Y%m%d-%H%M%S).txt}"
|
|
local filter_mode="${2:-all}"
|
|
local filter_arg="${3:-}"
|
|
|
|
{
|
|
echo "╔════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ PHP-FPM SERVER ANALYSIS REPORT ║"
|
|
echo "╚════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
echo "Generated: $(date)"
|
|
echo ""
|
|
|
|
# Server Information
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "SERVER INFORMATION"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo "Total RAM: $(free -h | awk '/^Mem:/ {print $2}')"
|
|
echo "CPU Cores: $(nproc)"
|
|
echo "Total Accounts: $(get_total_account_count)"
|
|
echo "Total Domains: $(get_total_domain_count)"
|
|
echo ""
|
|
|
|
# Analysis Results
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "ANALYSIS RESULTS"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
local analysis_result
|
|
analysis_result=$(analyze_entire_server)
|
|
|
|
local total_domains domains_with_issues critical high medium low
|
|
total_domains=$(echo "$analysis_result" | cut -d'|' -f1)
|
|
domains_with_issues=$(echo "$analysis_result" | cut -d'|' -f2)
|
|
critical=$(echo "$analysis_result" | cut -d'|' -f3)
|
|
high=$(echo "$analysis_result" | cut -d'|' -f4)
|
|
medium=$(echo "$analysis_result" | cut -d'|' -f5)
|
|
low=$(echo "$analysis_result" | cut -d'|' -f6)
|
|
|
|
echo "Total Domains Analyzed: $total_domains"
|
|
echo "Domains with Issues: $domains_with_issues"
|
|
echo ""
|
|
echo "Issue Summary:"
|
|
echo " CRITICAL: $critical"
|
|
echo " HIGH: $high"
|
|
echo " MEDIUM: $medium"
|
|
echo " LOW: $low"
|
|
echo ""
|
|
|
|
# Health Status
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "SERVER HEALTH STATUS"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>/dev/null)
|
|
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo "Total Server RAM: ${total_ram_mb}MB"
|
|
echo "Current FPM Capacity: ${total_required_mb}MB (${percentage}% of RAM)"
|
|
echo "Server Status: $status"
|
|
echo ""
|
|
|
|
# Recommendations
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "RECOMMENDATIONS"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
if [ "$domains_with_issues" -gt 0 ]; then
|
|
echo "1. Apply recommended optimizations to $domains_with_issues domain(s)"
|
|
if [ "$critical" -gt 0 ]; then
|
|
echo " - URGENT: Address $critical CRITICAL issue(s)"
|
|
fi
|
|
if [ "$high" -gt 0 ]; then
|
|
echo " - HIGH PRIORITY: Address $high HIGH severity issue(s)"
|
|
fi
|
|
else
|
|
echo "No issues detected - server configuration is optimal"
|
|
fi
|
|
|
|
case "$status" in
|
|
CRITICAL)
|
|
echo "2. URGENT: Review memory allocation - server at OOM risk!"
|
|
;;
|
|
WARNING)
|
|
echo "2. Review memory allocation - consider reducing max_children"
|
|
;;
|
|
CAUTION)
|
|
echo "2. Monitor memory usage - consider minor adjustments"
|
|
;;
|
|
HEALTHY)
|
|
echo "2. Continue monitoring - no immediate action needed"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
|
|
# Change History (if available)
|
|
if [ -n "$EXECUTOR_CHANGE_LOG" ] && [ -f "$EXECUTOR_CHANGE_LOG" ]; then
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "RECENT CHANGES"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
tail -20 "$EXECUTOR_CHANGE_LOG"
|
|
echo ""
|
|
fi
|
|
|
|
# Footer
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "Report generated by PHP-FPM Optimizer - Phase 3"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
|
|
} | tee "$report_file"
|
|
|
|
echo ""
|
|
echo "Report saved to: $report_file"
|
|
}
|
|
|
|
# Generate domain-specific report
|
|
generate_domain_report() {
|
|
local domain="$1"
|
|
local report_file="${2:-/tmp/php-optimizer-${domain}-report-$(date +%Y%m%d-%H%M%S).txt}"
|
|
|
|
local owner
|
|
owner=$(find_domain_owner "$domain")
|
|
|
|
if [ -z "$owner" ]; then
|
|
return 1
|
|
fi
|
|
|
|
{
|
|
echo "╔════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ PHP-FPM DOMAIN ANALYSIS REPORT ║"
|
|
echo "╚════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
echo "Domain: $domain"
|
|
echo "Owner: $owner"
|
|
echo "Generated: $(date)"
|
|
echo ""
|
|
|
|
# Domain Information
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "DOMAIN INFORMATION"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$owner" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ]; then
|
|
echo "Pool Config: $pool_config"
|
|
echo ""
|
|
echo "Current Settings:"
|
|
grep "^pm" "$pool_config" | sed 's/^/ /'
|
|
echo ""
|
|
fi
|
|
|
|
# Analysis
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "ANALYSIS"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
local issues
|
|
issues=$(detect_php_config_issues "$owner" "$domain" 2>/dev/null)
|
|
|
|
if [ -z "$issues" ] || [ "$(echo "$issues" | wc -l)" -eq 0 ]; then
|
|
echo "No issues detected - configuration is optimal"
|
|
else
|
|
echo "Issues Found:"
|
|
echo ""
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ -z "$issue_type" ] && continue
|
|
echo "[$severity] $message"
|
|
echo " → $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Recommendations
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo "RECOMMENDATIONS"
|
|
echo "═══════════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
local total_ram_mb
|
|
total_ram_mb=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
local improved_result
|
|
improved_result=$(calculate_optimal_php_settings "$owner" "$total_ram_mb" 2>/dev/null)
|
|
|
|
if [ -n "$improved_result" ]; then
|
|
local improved_max_children improved_pm_mode improved_reason
|
|
improved_max_children=$(echo "$improved_result" | cut -d'|' -f1)
|
|
improved_pm_mode=$(echo "$improved_result" | cut -d'|' -f2)
|
|
improved_reason=$(echo "$improved_result" | cut -d'|' -f5)
|
|
|
|
echo "Recommended pm.max_children: $improved_max_children"
|
|
echo "Recommended pm mode: $improved_pm_mode"
|
|
echo "Reason: $improved_reason"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
} | tee "$report_file"
|
|
|
|
echo "Report saved to: $report_file"
|
|
}
|
|
|
|
# ============================================================================
|
|
# BATCH OPERATIONS
|
|
# ============================================================================
|
|
|
|
# Perform batch operation on multiple domains
|
|
batch_operation() {
|
|
local operation="$1" # optimize, analyze, health_check
|
|
local filter_mode="${2:-needs_optimization}"
|
|
local filter_arg="${3:-}"
|
|
local require_confirmation="${4:-true}"
|
|
|
|
local -a target_domains
|
|
mapfile -t target_domains < <(scan_entire_server "$filter_mode" "$filter_arg")
|
|
|
|
case "$operation" in
|
|
optimize)
|
|
echo "Planning server-wide optimization..."
|
|
plan_server_optimizations "$filter_mode" "$filter_arg"
|
|
|
|
if [ "$require_confirmation" = "true" ]; then
|
|
if ! confirm "Execute optimizations?"; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
execute_server_optimization_plan "${target_domains[@]}"
|
|
;;
|
|
analyze)
|
|
echo "Analyzing entire server..."
|
|
analyze_entire_server
|
|
;;
|
|
health_check)
|
|
echo "Performing health check on all domains..."
|
|
init_change_tracking
|
|
|
|
local total=${#target_domains[@]}
|
|
local current=0
|
|
|
|
for domain in "${target_domains[@]}"; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
current=$((current + 1))
|
|
display_progress "$current" "$total"
|
|
|
|
local owner
|
|
owner=$(find_domain_owner "$domain")
|
|
[ -n "$owner" ] && perform_health_check "$owner" "$domain" >/dev/null 2>&1
|
|
done
|
|
|
|
echo ""
|
|
;;
|
|
esac
|
|
|
|
return $?
|
|
}
|
|
|
|
# ============================================================================
|
|
# EXPORT ALL FUNCTIONS
|
|
# ============================================================================
|
|
|
|
export -f scan_entire_server
|
|
export -f analyze_entire_server
|
|
export -f plan_server_optimizations
|
|
export -f execute_server_optimization_plan
|
|
export -f generate_server_report
|
|
export -f generate_domain_report
|
|
export -f batch_operation
|