Fix critical bugs and add domain-by-domain batch analyzer
- 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>
This commit is contained in:
Executable
+580
@@ -0,0 +1,580 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PHP-FPM Action Executor Module
|
||||||
|
# Handles optimization application, change tracking, and rollback
|
||||||
|
# Part of PHP Optimizer - Phase 3 Refactoring
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CHANGE TRACKING
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Initialize change tracking for a session
|
||||||
|
init_change_tracking() {
|
||||||
|
local session_id="${1:-$(date +%s)}"
|
||||||
|
local tracking_dir="/var/log/php-optimizer/changes"
|
||||||
|
|
||||||
|
mkdir -p "$tracking_dir" 2>/dev/null || true
|
||||||
|
export EXECUTOR_SESSION_ID="$session_id"
|
||||||
|
export EXECUTOR_TRACKING_DIR="$tracking_dir"
|
||||||
|
export EXECUTOR_CHANGE_LOG="${tracking_dir}/change-${session_id}.log"
|
||||||
|
|
||||||
|
> "$EXECUTOR_CHANGE_LOG" # Clear the log file
|
||||||
|
}
|
||||||
|
|
||||||
|
# Log a change for audit trail
|
||||||
|
log_change() {
|
||||||
|
local domain="$1"
|
||||||
|
local action="$2"
|
||||||
|
local before="$3"
|
||||||
|
local after="$4"
|
||||||
|
local status="${5:-pending}"
|
||||||
|
|
||||||
|
if [ -z "$EXECUTOR_CHANGE_LOG" ]; then
|
||||||
|
init_change_tracking
|
||||||
|
fi
|
||||||
|
|
||||||
|
local timestamp
|
||||||
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
cat >> "$EXECUTOR_CHANGE_LOG" << EOF
|
||||||
|
$timestamp|$domain|$action|$status
|
||||||
|
Before: $before
|
||||||
|
After: $after
|
||||||
|
---
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get change history
|
||||||
|
get_change_history() {
|
||||||
|
local domain="${1:-all}"
|
||||||
|
local limit="${2:-50}"
|
||||||
|
|
||||||
|
if [ -z "$EXECUTOR_TRACKING_DIR" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$domain" = "all" ]; then
|
||||||
|
tail -n "$limit" "$EXECUTOR_TRACKING_DIR"/change-*.log 2>/dev/null || true
|
||||||
|
else
|
||||||
|
grep "^[^|]*|$domain|" "$EXECUTOR_TRACKING_DIR"/change-*.log 2>/dev/null | tail -n "$limit" || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of all changes from a specific date
|
||||||
|
get_changes_since() {
|
||||||
|
local since_date="$1"
|
||||||
|
[ -z "$since_date" ] && return 1
|
||||||
|
|
||||||
|
if [ -z "$EXECUTOR_TRACKING_DIR" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
find "$EXECUTOR_TRACKING_DIR" -name "change-*.log" -newer /tmp/php-optimizer-since-"$since_date" 2>/dev/null | \
|
||||||
|
xargs cat 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# BACKUP & ROLLBACK
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Create backup of a domain's FPM pool config before making changes
|
||||||
|
backup_domain_config() {
|
||||||
|
local domain="$1"
|
||||||
|
local username="${2:-}"
|
||||||
|
|
||||||
|
local pool_config
|
||||||
|
if [ -n "$username" ]; then
|
||||||
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
||||||
|
else
|
||||||
|
pool_config=$(find_fpm_pool_by_domain "$domain" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local backup_dir="/var/lib/php-optimizer/backups"
|
||||||
|
mkdir -p "$backup_dir" 2>/dev/null || true
|
||||||
|
|
||||||
|
local backup_file
|
||||||
|
backup_file="${backup_dir}/${domain}-$(date +%Y%m%d-%H%M%S).conf"
|
||||||
|
|
||||||
|
cp "$pool_config" "$backup_file" 2>/dev/null || return 1
|
||||||
|
echo "$backup_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rollback a domain's config to a specific backup
|
||||||
|
rollback_domain_config() {
|
||||||
|
local domain="$1"
|
||||||
|
local backup_file="$2"
|
||||||
|
|
||||||
|
[ -z "$domain" ] || [ -z "$backup_file" ] && return 1
|
||||||
|
[ ! -f "$backup_file" ] && return 1
|
||||||
|
|
||||||
|
local pool_config
|
||||||
|
pool_config=$(find_fpm_pool_by_domain "$domain" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$backup_file" "$pool_config" 2>/dev/null || return 1
|
||||||
|
log_change "$domain" "rollback" "current" "restored_from_backup"
|
||||||
|
|
||||||
|
# Reload PHP-FPM
|
||||||
|
reload_php_fpm
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CONFIGURATION MODIFICATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Update a PHP pool configuration parameter
|
||||||
|
update_pool_parameter() {
|
||||||
|
local pool_config="$1"
|
||||||
|
local parameter="$2"
|
||||||
|
local value="$3"
|
||||||
|
|
||||||
|
[ -z "$pool_config" ] || [ -z "$parameter" ] || [ -z "$value" ] && return 1
|
||||||
|
[ ! -f "$pool_config" ] && return 1
|
||||||
|
|
||||||
|
# Check if parameter exists
|
||||||
|
if grep -q "^${parameter}\s*=" "$pool_config"; then
|
||||||
|
# Update existing parameter
|
||||||
|
sed -i.bak "s/^${parameter}\s*=.*/${parameter} = ${value}/" "$pool_config"
|
||||||
|
else
|
||||||
|
# Add new parameter
|
||||||
|
echo "${parameter} = ${value}" >> "$pool_config"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update multiple pool parameters at once
|
||||||
|
update_pool_parameters() {
|
||||||
|
local pool_config="$1"
|
||||||
|
shift # Remove first argument
|
||||||
|
local -a params=("$@")
|
||||||
|
|
||||||
|
[ -f "$pool_config" ] || return 1
|
||||||
|
|
||||||
|
# Create backup before making multiple changes
|
||||||
|
local backup_file
|
||||||
|
backup_file=$(backup_domain_config "temp" 2>/dev/null) || backup_file="${pool_config}.backup"
|
||||||
|
cp "$pool_config" "$backup_file" 2>/dev/null
|
||||||
|
|
||||||
|
local all_success=true
|
||||||
|
for param_pair in "${params[@]}"; do
|
||||||
|
local param_name param_value
|
||||||
|
param_name=$(echo "$param_pair" | cut -d'=' -f1)
|
||||||
|
param_value=$(echo "$param_pair" | cut -d'=' -f2)
|
||||||
|
|
||||||
|
if ! update_pool_parameter "$pool_config" "$param_name" "$param_value"; then
|
||||||
|
all_success=false
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$all_success" = false ]; then
|
||||||
|
# Restore backup on failure
|
||||||
|
cp "$backup_file" "$pool_config" 2>/dev/null
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply max_children optimization
|
||||||
|
apply_max_children_optimization() {
|
||||||
|
local domain="$1"
|
||||||
|
local username="$2"
|
||||||
|
local new_max_children="$3"
|
||||||
|
|
||||||
|
local pool_config
|
||||||
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get current value for logging
|
||||||
|
local current_value
|
||||||
|
current_value=$(grep "^pm.max_children" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
current_value=${current_value:-unknown}
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
local backup_file
|
||||||
|
backup_file=$(backup_domain_config "$domain" "$username")
|
||||||
|
|
||||||
|
# Update the parameter
|
||||||
|
if ! update_pool_parameter "$pool_config" "pm.max_children" "$new_max_children"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log the change
|
||||||
|
log_change "$domain" "max_children" "$current_value" "$new_max_children" "completed"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply PM mode optimization
|
||||||
|
apply_pm_mode_optimization() {
|
||||||
|
local domain="$1"
|
||||||
|
local username="$2"
|
||||||
|
local pm_mode="$3"
|
||||||
|
local min_spare="${4:-10}"
|
||||||
|
local max_spare="${5:-20}"
|
||||||
|
|
||||||
|
local pool_config
|
||||||
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get current values for logging
|
||||||
|
local current_mode current_min current_max
|
||||||
|
current_mode=$(grep "^pm\s*=" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
current_min=$(grep "^pm.min_spare_servers" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
current_max=$(grep "^pm.max_spare_servers" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
local backup_file
|
||||||
|
backup_file=$(backup_domain_config "$domain" "$username")
|
||||||
|
|
||||||
|
# Update parameters
|
||||||
|
local params=(
|
||||||
|
"pm=$pm_mode"
|
||||||
|
"pm.min_spare_servers=$min_spare"
|
||||||
|
"pm.max_spare_servers=$max_spare"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ! update_pool_parameters "$pool_config" "${params[@]}"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log the change
|
||||||
|
log_change "$domain" "pm_mode" "$current_mode/$current_min/$current_max" "$pm_mode/$min_spare/$max_spare" "completed"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# OPTIMIZATION APPLICATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Apply optimization to a single domain
|
||||||
|
apply_optimization() {
|
||||||
|
local domain="$1"
|
||||||
|
local username="$2"
|
||||||
|
local optimization_type="${3:-all}" # all, max_children, pm_mode, opcache
|
||||||
|
local dry_run="${4:-false}"
|
||||||
|
|
||||||
|
if [ "$dry_run" = "true" ]; then
|
||||||
|
return 0 # Skip actual changes in dry-run mode
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$optimization_type" in
|
||||||
|
max_children)
|
||||||
|
apply_max_children_optimization "$domain" "$username" "$5" || return 1
|
||||||
|
;;
|
||||||
|
pm_mode)
|
||||||
|
apply_pm_mode_optimization "$domain" "$username" "$5" "$6" "$7" || return 1
|
||||||
|
;;
|
||||||
|
all)
|
||||||
|
# Apply all recommendations
|
||||||
|
if [ -n "$5" ]; then
|
||||||
|
apply_max_children_optimization "$domain" "$username" "$5" || return 1
|
||||||
|
fi
|
||||||
|
if [ -n "$6" ]; then
|
||||||
|
apply_pm_mode_optimization "$domain" "$username" "$6" "$7" "$8" || return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply optimizations to multiple domains (batch operation)
|
||||||
|
apply_batch_optimization() {
|
||||||
|
local -a domains=("$@")
|
||||||
|
local dry_run="${DRY_RUN:-false}"
|
||||||
|
local total_domains=${#domains[@]}
|
||||||
|
local current=0
|
||||||
|
local successful=0
|
||||||
|
local failed=0
|
||||||
|
|
||||||
|
init_change_tracking
|
||||||
|
|
||||||
|
for domain in "${domains[@]}"; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
|
current=$((current + 1))
|
||||||
|
show_enumeration_progress "$current" "$total_domains"
|
||||||
|
|
||||||
|
local username
|
||||||
|
username=$(find_domain_owner "$domain")
|
||||||
|
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
failed=$((failed + 1))
|
||||||
|
log_change "$domain" "batch_optimization" "unknown_user" "skipped" "failed"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply optimization
|
||||||
|
if apply_optimization "$domain" "$username" "all" "$dry_run"; then
|
||||||
|
successful=$((successful + 1))
|
||||||
|
log_change "$domain" "batch_optimization" "started" "completed" "completed"
|
||||||
|
else
|
||||||
|
failed=$((failed + 1))
|
||||||
|
log_change "$domain" "batch_optimization" "attempted" "failed" "failed"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
return $((failed > 0 ? 1 : 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# VERIFICATION & VALIDATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Verify that changes were applied correctly
|
||||||
|
verify_applied_changes() {
|
||||||
|
local domain="$1"
|
||||||
|
local username="$2"
|
||||||
|
local expected_max_children="${3:-}"
|
||||||
|
local expected_pm_mode="${4:-}"
|
||||||
|
|
||||||
|
local pool_config
|
||||||
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local verify_success=true
|
||||||
|
|
||||||
|
# Verify max_children if expected
|
||||||
|
if [ -n "$expected_max_children" ]; then
|
||||||
|
local actual_max_children
|
||||||
|
actual_max_children=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
|
||||||
|
if [ "$actual_max_children" != "$expected_max_children" ]; then
|
||||||
|
verify_success=false
|
||||||
|
echo "max_children mismatch: expected $expected_max_children, got $actual_max_children"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify PM mode if expected
|
||||||
|
if [ -n "$expected_pm_mode" ]; then
|
||||||
|
local actual_pm_mode
|
||||||
|
actual_pm_mode=$(grep "^pm\s*=" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
|
||||||
|
if [ "$actual_pm_mode" != "$expected_pm_mode" ]; then
|
||||||
|
verify_success=false
|
||||||
|
echo "pm mode mismatch: expected $expected_pm_mode, got $actual_pm_mode"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$verify_success" = true ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if changes are valid (syntax, no conflicts)
|
||||||
|
validate_pool_config() {
|
||||||
|
local pool_config="$1"
|
||||||
|
|
||||||
|
[ ! -f "$pool_config" ] && return 1
|
||||||
|
|
||||||
|
# Basic syntax check
|
||||||
|
if grep -q "^[a-z_]*\s*=\s*[^;]*$" "$pool_config"; then
|
||||||
|
# Check for common issues
|
||||||
|
if grep -q "^pm.max_children\s*=\s*0" "$pool_config"; then
|
||||||
|
return 1 # max_children cannot be 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHP-FPM SERVICE OPERATIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Reload PHP-FPM to apply changes
|
||||||
|
reload_php_fpm() {
|
||||||
|
local php_version="${1:-}"
|
||||||
|
|
||||||
|
# Try common PHP-FPM service names
|
||||||
|
local service_names=("php-fpm" "php7.4-fpm" "php8.0-fpm" "php8.1-fpm" "php8.2-fpm" "php8.3-fpm")
|
||||||
|
|
||||||
|
if [ -n "$php_version" ]; then
|
||||||
|
service_names=("php${php_version}-fpm" "php-fpm")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for service in "${service_names[@]}"; do
|
||||||
|
if systemctl is-active --quiet "$service" 2>/dev/null; then
|
||||||
|
systemctl reload "$service" 2>/dev/null || service "$service" reload 2>/dev/null
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Fallback: try service command
|
||||||
|
service php-fpm reload 2>/dev/null || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restart PHP-FPM (full restart, not just reload)
|
||||||
|
restart_php_fpm() {
|
||||||
|
local php_version="${1:-}"
|
||||||
|
|
||||||
|
local service_names=("php-fpm" "php7.4-fpm" "php8.0-fpm" "php8.1-fpm" "php8.2-fpm" "php8.3-fpm")
|
||||||
|
|
||||||
|
if [ -n "$php_version" ]; then
|
||||||
|
service_names=("php${php_version}-fpm" "php-fpm")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for service in "${service_names[@]}"; do
|
||||||
|
if systemctl is-active --quiet "$service" 2>/dev/null; then
|
||||||
|
systemctl restart "$service" 2>/dev/null || service "$service" restart 2>/dev/null
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get PHP-FPM service status
|
||||||
|
get_php_fpm_status() {
|
||||||
|
local service_names=("php-fpm" "php7.4-fpm" "php8.0-fpm" "php8.1-fpm" "php8.2-fpm" "php8.3-fpm")
|
||||||
|
|
||||||
|
for service in "${service_names[@]}"; do
|
||||||
|
if systemctl is-active --quiet "$service" 2>/dev/null; then
|
||||||
|
systemctl status "$service"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DRY-RUN MODE (PREVIEW CHANGES)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Preview what changes would be applied (without making them)
|
||||||
|
preview_changes() {
|
||||||
|
local domain="$1"
|
||||||
|
local username="$2"
|
||||||
|
local -a changes=("${@:3}")
|
||||||
|
|
||||||
|
local pool_config
|
||||||
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "PREVIEW: Changes that would be applied to $domain:"
|
||||||
|
echo ""
|
||||||
|
echo "Config file: $pool_config"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for change in "${changes[@]}"; do
|
||||||
|
local param_name param_new_value
|
||||||
|
param_name=$(echo "$change" | cut -d'=' -f1)
|
||||||
|
param_new_value=$(echo "$change" | cut -d'=' -f2)
|
||||||
|
|
||||||
|
local current_value
|
||||||
|
current_value=$(grep "^${param_name}\s*=" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
|
||||||
|
if [ -z "$current_value" ]; then
|
||||||
|
echo " + $param_name = $param_new_value (NEW)"
|
||||||
|
else
|
||||||
|
echo " - $param_name = $current_value"
|
||||||
|
echo " + $param_name = $param_new_value"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HELPER FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Find FPM pool config for a domain
|
||||||
|
find_fpm_pool_config() {
|
||||||
|
local username="$1"
|
||||||
|
local domain="$2"
|
||||||
|
|
||||||
|
# Try using existing function if available
|
||||||
|
if type find_fpm_pool_config_internal >/dev/null 2>&1; then
|
||||||
|
find_fpm_pool_config_internal "$username" "$domain"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback: search common locations
|
||||||
|
local common_paths=(
|
||||||
|
"/etc/php-fpm.d/${username}.conf"
|
||||||
|
"/etc/php/7.4/fpm/pool.d/${username}.conf"
|
||||||
|
"/etc/php/8.0/fpm/pool.d/${username}.conf"
|
||||||
|
"/etc/php/8.1/fpm/pool.d/${username}.conf"
|
||||||
|
"/etc/php/8.2/fpm/pool.d/${username}.conf"
|
||||||
|
"/etc/php/8.3/fpm/pool.d/${username}.conf"
|
||||||
|
)
|
||||||
|
|
||||||
|
for path in "${common_paths[@]}"; do
|
||||||
|
if [ -f "$path" ]; then
|
||||||
|
echo "$path"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find FPM pool config by domain name
|
||||||
|
find_fpm_pool_by_domain() {
|
||||||
|
local domain="$1"
|
||||||
|
|
||||||
|
local owner
|
||||||
|
owner=$(find_domain_owner "$domain")
|
||||||
|
|
||||||
|
if [ -n "$owner" ]; then
|
||||||
|
find_fpm_pool_config "$owner" "$domain"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# EXPORT ALL FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
export -f init_change_tracking
|
||||||
|
export -f log_change
|
||||||
|
export -f get_change_history
|
||||||
|
export -f get_changes_since
|
||||||
|
export -f backup_domain_config
|
||||||
|
export -f rollback_domain_config
|
||||||
|
export -f update_pool_parameter
|
||||||
|
export -f update_pool_parameters
|
||||||
|
export -f apply_max_children_optimization
|
||||||
|
export -f apply_pm_mode_optimization
|
||||||
|
export -f apply_optimization
|
||||||
|
export -f apply_batch_optimization
|
||||||
|
export -f verify_applied_changes
|
||||||
|
export -f validate_pool_config
|
||||||
|
export -f reload_php_fpm
|
||||||
|
export -f restart_php_fpm
|
||||||
|
export -f get_php_fpm_status
|
||||||
|
export -f preview_changes
|
||||||
|
export -f find_fpm_pool_config
|
||||||
|
export -f find_fpm_pool_by_domain
|
||||||
@@ -59,6 +59,7 @@ analyze_memory_exhausted_errors() {
|
|||||||
# Find errors in last N days
|
# Find errors in last N days
|
||||||
local count
|
local count
|
||||||
count=$(find "$log_file" -mtime -"$days" -exec grep -c "Allowed memory size.*exhausted" {} \; 2>/dev/null || echo "0")
|
count=$(find "$log_file" -mtime -"$days" -exec grep -c "Allowed memory size.*exhausted" {} \; 2>/dev/null || echo "0")
|
||||||
|
count="${count:-0}"
|
||||||
|
|
||||||
if [ "$count" -gt 0 ]; then
|
if [ "$count" -gt 0 ]; then
|
||||||
total_count=$((total_count + count))
|
total_count=$((total_count + count))
|
||||||
@@ -651,6 +652,7 @@ detect_php_config_issues() {
|
|||||||
memory_errors=$(analyze_memory_exhausted_errors "$username" 7)
|
memory_errors=$(analyze_memory_exhausted_errors "$username" 7)
|
||||||
local memory_error_count
|
local memory_error_count
|
||||||
memory_error_count=$(get_field "$(echo "$memory_errors" | grep "TOTAL")" 1)
|
memory_error_count=$(get_field "$(echo "$memory_errors" | grep "TOTAL")" 1)
|
||||||
|
memory_error_count="${memory_error_count:-0}"
|
||||||
|
|
||||||
if [ "$memory_error_count" -gt 0 ]; then
|
if [ "$memory_error_count" -gt 0 ]; then
|
||||||
issues+="MEMORY|HIGH|Memory exhausted errors occurred $memory_error_count times in last 7 days|Increase memory_limit or optimize code"$'\n'
|
issues+="MEMORY|HIGH|Memory exhausted errors occurred $memory_error_count times in last 7 days|Increase memory_limit or optimize code"$'\n'
|
||||||
|
|||||||
Executable
+554
@@ -0,0 +1,554 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PHP-FPM Server Scanner Module
|
||||||
|
# Handles enumeration of accounts/domains across entire server with filtering
|
||||||
|
# Part of PHP Optimizer - Phase 3 Refactoring
|
||||||
|
# Ensures full server-wide scanning and action capability
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ACCOUNT ENUMERATION FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Enumerate all accounts/users on the server
|
||||||
|
enumerate_all_accounts() {
|
||||||
|
local force_refresh="${1:-false}"
|
||||||
|
local cache_file="/tmp/php-scanner-accounts-cache-$$"
|
||||||
|
|
||||||
|
# Return cached results if available (unless force_refresh=true)
|
||||||
|
if [ "$force_refresh" != "true" ] && [ -f "$cache_file" ]; then
|
||||||
|
cat "$cache_file"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delegate to user-manager.sh if available
|
||||||
|
if type list_all_users >/dev/null 2>&1; then
|
||||||
|
local accounts
|
||||||
|
accounts=$(list_all_users)
|
||||||
|
if [ -n "$accounts" ]; then
|
||||||
|
echo "$accounts" | tee "$cache_file"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback enumeration if user-manager.sh not available
|
||||||
|
case "${SYS_CONTROL_PANEL:-unknown}" in
|
||||||
|
cpanel)
|
||||||
|
_enumerate_cpanel_accounts | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
plesk)
|
||||||
|
_enumerate_plesk_accounts | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
interworx)
|
||||||
|
_enumerate_interworx_accounts | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_enumerate_system_accounts | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# cPanel account enumeration
|
||||||
|
_enumerate_cpanel_accounts() {
|
||||||
|
local cpanel_users_dir="${SYS_CPANEL_USERS_DIR:-/var/cpanel/users}"
|
||||||
|
if [ -d "$cpanel_users_dir" ]; then
|
||||||
|
ls "$cpanel_users_dir" 2>/dev/null | grep -v "^system\|^root\|^\." || true
|
||||||
|
else
|
||||||
|
awk -F: '{print $2}' /etc/trueuserdomains 2>/dev/null | sort -u || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Plesk account enumeration
|
||||||
|
_enumerate_plesk_accounts() {
|
||||||
|
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
|
||||||
|
mysql -Ns psa -e "SELECT login FROM sys_users WHERE type='user'" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
find /var/www/vhosts -maxdepth 1 -type d -printf "%f\n" 2>/dev/null | \
|
||||||
|
grep -v "^system$\|^default$\|^chroot$\|^\.skel$\|^fs$\|^fs-passwd$\|^\." || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# InterWorx account enumeration
|
||||||
|
_enumerate_interworx_accounts() {
|
||||||
|
if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then
|
||||||
|
/usr/local/interworx/bin/listaccounts.pex --output user 2>/dev/null || true
|
||||||
|
else
|
||||||
|
if [ -d "/etc/httpd/conf.d" ]; then
|
||||||
|
grep -h "^[[:space:]]*SuexecUserGroup" /etc/httpd/conf.d/vhost_*.conf 2>/dev/null | \
|
||||||
|
awk '{print $2}' | sort -u || true
|
||||||
|
else
|
||||||
|
find /home -maxdepth 1 -type d ! -name "home" ! -name "interworx" -printf "%f\n" 2>/dev/null | sort
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# System-wide account enumeration (fallback)
|
||||||
|
_enumerate_system_accounts() {
|
||||||
|
awk -F: '($3 >= 500) && ($3 != 65534) {print $1}' /etc/passwd 2>/dev/null | \
|
||||||
|
grep -v "^root\|^nobody\|^ntp\|^mysql\|^www-data\|^apache\|^nginx" | \
|
||||||
|
sort -u || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOMAIN ENUMERATION FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Enumerate all domains for a specific user/account
|
||||||
|
enumerate_user_domains() {
|
||||||
|
[ -z "$1" ] && return 1
|
||||||
|
local username="$1"
|
||||||
|
local force_refresh="${2:-false}"
|
||||||
|
local cache_file="/tmp/php-scanner-domains-${username}-cache-$$"
|
||||||
|
|
||||||
|
# Return cached results if available (unless force_refresh=true)
|
||||||
|
if [ "$force_refresh" != "true" ] && [ -f "$cache_file" ]; then
|
||||||
|
cat "$cache_file"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delegate to user-manager.sh if available
|
||||||
|
if type get_user_domains >/dev/null 2>&1; then
|
||||||
|
local domains
|
||||||
|
domains=$(get_user_domains "$username")
|
||||||
|
if [ -n "$domains" ]; then
|
||||||
|
echo "$domains" | tee "$cache_file"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback domain enumeration
|
||||||
|
case "${SYS_CONTROL_PANEL:-unknown}" in
|
||||||
|
cpanel)
|
||||||
|
_enumerate_cpanel_domains "$username" | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
plesk)
|
||||||
|
_enumerate_plesk_domains "$username" | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
interworx)
|
||||||
|
_enumerate_interworx_domains "$username" | tee "$cache_file"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# cPanel domain enumeration
|
||||||
|
_enumerate_cpanel_domains() {
|
||||||
|
local username="$1"
|
||||||
|
[ -z "$username" ] && return 1
|
||||||
|
|
||||||
|
# Primary domain
|
||||||
|
grep ": ${username}$" /etc/trueuserdomains 2>/dev/null | cut -d: -f1 || true
|
||||||
|
|
||||||
|
# Addon domains
|
||||||
|
if [ -f "/etc/userdatadomains" ]; then
|
||||||
|
grep "==${username}$" /etc/userdatadomains 2>/dev/null | cut -d: -f1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Plesk domain enumeration
|
||||||
|
_enumerate_plesk_domains() {
|
||||||
|
local username="$1"
|
||||||
|
[ -z "$username" ] && return 1
|
||||||
|
|
||||||
|
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
|
||||||
|
mysql -Ns psa -e "SELECT d.name FROM domains d JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null || true
|
||||||
|
elif [ -x "/usr/local/psa/bin/plesk" ]; then
|
||||||
|
/usr/local/psa/bin/plesk bin site --list 2>/dev/null | grep -i "$username" || true
|
||||||
|
elif [ -d "/var/www/vhosts/$username" ]; then
|
||||||
|
echo "$username"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# InterWorx domain enumeration
|
||||||
|
_enumerate_interworx_domains() {
|
||||||
|
local username="$1"
|
||||||
|
[ -z "$username" ] && return 1
|
||||||
|
|
||||||
|
if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then
|
||||||
|
/usr/local/interworx/bin/listaccounts.pex 2>/dev/null | \
|
||||||
|
awk -v user="$username" '$1 == user {print $2}'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "/etc/httpd/conf.d" ]; then
|
||||||
|
grep -l "SuexecUserGroup ${username}" /etc/httpd/conf.d/vhost_*.conf 2>/dev/null | \
|
||||||
|
sed 's|.*/vhost_||; s|\.conf$||' | \
|
||||||
|
grep -vF "${username}." 2>/dev/null | \
|
||||||
|
sort -u
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enumerate ALL domains on the server (across all users)
|
||||||
|
enumerate_all_domains() {
|
||||||
|
local force_refresh="${1:-false}"
|
||||||
|
local cache_file="/tmp/php-scanner-all-domains-cache-$$"
|
||||||
|
local progress_file="/tmp/php-scanner-progress-$$"
|
||||||
|
|
||||||
|
# Return cached results if available (unless force_refresh=true)
|
||||||
|
if [ "$force_refresh" != "true" ] && [ -f "$cache_file" ]; then
|
||||||
|
cat "$cache_file"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
> "$progress_file" # Clear progress file
|
||||||
|
local users
|
||||||
|
local domain_list=""
|
||||||
|
local user_count=0
|
||||||
|
local current_user=0
|
||||||
|
|
||||||
|
users=$(enumerate_all_accounts)
|
||||||
|
user_count=$(echo "$users" | wc -l)
|
||||||
|
|
||||||
|
while IFS= read -r username; do
|
||||||
|
[ -z "$username" ] && continue
|
||||||
|
|
||||||
|
current_user=$((current_user + 1))
|
||||||
|
echo "$current_user/$user_count: $username" >> "$progress_file"
|
||||||
|
|
||||||
|
local domains
|
||||||
|
domains=$(enumerate_user_domains "$username")
|
||||||
|
if [ -n "$domains" ]; then
|
||||||
|
domain_list="${domain_list}${domains}"$'\n'
|
||||||
|
fi
|
||||||
|
done <<< "$users"
|
||||||
|
|
||||||
|
# Deduplicate and sort
|
||||||
|
echo "$domain_list" | sort -u | grep -v "^$" | tee "$cache_file"
|
||||||
|
|
||||||
|
rm -f "$progress_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# FILTERING FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Filter accounts by name pattern
|
||||||
|
filter_accounts_by_name() {
|
||||||
|
local pattern="$1"
|
||||||
|
[ -z "$pattern" ] && return 1
|
||||||
|
|
||||||
|
local all_accounts
|
||||||
|
all_accounts=$(enumerate_all_accounts)
|
||||||
|
|
||||||
|
echo "$all_accounts" | grep -i "$pattern" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter accounts by resource usage threshold
|
||||||
|
filter_accounts_by_threshold() {
|
||||||
|
local threshold_mb="${1:-1000}"
|
||||||
|
local direction="${2:-above}" # above or below
|
||||||
|
|
||||||
|
local all_accounts
|
||||||
|
all_accounts=$(enumerate_all_accounts)
|
||||||
|
|
||||||
|
local filtered=""
|
||||||
|
while IFS= read -r username; do
|
||||||
|
[ -z "$username" ] && continue
|
||||||
|
|
||||||
|
local usage_mb
|
||||||
|
usage_mb=$(get_account_disk_usage "$username")
|
||||||
|
|
||||||
|
if [ "$direction" = "above" ] && [ "$usage_mb" -gt "$threshold_mb" ]; then
|
||||||
|
filtered="${filtered}${username}"$'\n'
|
||||||
|
elif [ "$direction" = "below" ] && [ "$usage_mb" -lt "$threshold_mb" ]; then
|
||||||
|
filtered="${filtered}${username}"$'\n'
|
||||||
|
fi
|
||||||
|
done <<< "$all_accounts"
|
||||||
|
|
||||||
|
echo "$filtered" | grep -v "^$"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter domains by name pattern
|
||||||
|
filter_domains_by_name() {
|
||||||
|
local pattern="$1"
|
||||||
|
[ -z "$pattern" ] && return 1
|
||||||
|
|
||||||
|
local all_domains
|
||||||
|
all_domains=$(enumerate_all_domains)
|
||||||
|
|
||||||
|
echo "$all_domains" | grep -i "$pattern" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter domains by traffic level
|
||||||
|
filter_domains_by_traffic() {
|
||||||
|
local min_requests="${1:-100}" # Minimum requests per second
|
||||||
|
local direction="${2:-above}" # above or below
|
||||||
|
|
||||||
|
local all_domains
|
||||||
|
all_domains=$(enumerate_all_domains)
|
||||||
|
|
||||||
|
local filtered=""
|
||||||
|
while IFS= read -r domain; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
|
local peak_concurrent
|
||||||
|
peak_concurrent=$(get_domain_peak_concurrent "$domain")
|
||||||
|
|
||||||
|
if [ "$direction" = "above" ] && [ "$peak_concurrent" -gt "$min_requests" ]; then
|
||||||
|
filtered="${filtered}${domain}"$'\n'
|
||||||
|
elif [ "$direction" = "below" ] && [ "$peak_concurrent" -lt "$min_requests" ]; then
|
||||||
|
filtered="${filtered}${domain}"$'\n'
|
||||||
|
fi
|
||||||
|
done <<< "$all_domains"
|
||||||
|
|
||||||
|
echo "$filtered" | grep -v "^$"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter domains by optimization status
|
||||||
|
filter_domains_by_optimization_status() {
|
||||||
|
local status="${1:-needs_optimization}" # needs_optimization or already_optimized
|
||||||
|
|
||||||
|
local all_domains
|
||||||
|
all_domains=$(enumerate_all_domains)
|
||||||
|
|
||||||
|
local filtered=""
|
||||||
|
while IFS= read -r domain; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
|
local is_optimized
|
||||||
|
is_optimized=$(is_domain_optimized "$domain")
|
||||||
|
|
||||||
|
if [ "$status" = "needs_optimization" ] && [ "$is_optimized" = "0" ]; then
|
||||||
|
filtered="${filtered}${domain}"$'\n'
|
||||||
|
elif [ "$status" = "already_optimized" ] && [ "$is_optimized" = "1" ]; then
|
||||||
|
filtered="${filtered}${domain}"$'\n'
|
||||||
|
fi
|
||||||
|
done <<< "$all_domains"
|
||||||
|
|
||||||
|
echo "$filtered" | grep -v "^$"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOMAIN INFORMATION FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Get comprehensive PHP-FPM information for a domain
|
||||||
|
get_domain_php_info() {
|
||||||
|
local domain="$1"
|
||||||
|
[ -z "$domain" ] && return 1
|
||||||
|
|
||||||
|
local owner username pool_name pool_path
|
||||||
|
|
||||||
|
# Find domain owner
|
||||||
|
owner=$(find_domain_owner "$domain")
|
||||||
|
[ -z "$owner" ] && return 1
|
||||||
|
|
||||||
|
# Find PHP pool
|
||||||
|
pool_name=$(php_detector_get_pool_name "$domain")
|
||||||
|
pool_path=$(php_detector_get_pool_config "$domain")
|
||||||
|
|
||||||
|
# Return info in structured format
|
||||||
|
cat << EOF
|
||||||
|
domain=$domain
|
||||||
|
owner=$owner
|
||||||
|
pool_name=$pool_name
|
||||||
|
pool_path=$pool_path
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get disk usage for an account
|
||||||
|
get_account_disk_usage() {
|
||||||
|
local username="$1"
|
||||||
|
[ -z "$username" ] && return 1
|
||||||
|
|
||||||
|
case "${SYS_CONTROL_PANEL:-unknown}" in
|
||||||
|
cpanel)
|
||||||
|
_get_cpanel_account_usage "$username"
|
||||||
|
;;
|
||||||
|
plesk)
|
||||||
|
_get_plesk_account_usage "$username"
|
||||||
|
;;
|
||||||
|
interworx)
|
||||||
|
_get_interworx_account_usage "$username"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_get_system_account_usage "$username"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_cpanel_account_usage() {
|
||||||
|
local username="$1"
|
||||||
|
local home="/home/$username"
|
||||||
|
if [ -d "$home" ]; then
|
||||||
|
du -sb "$home" 2>/dev/null | awk '{printf "%.0f", $1/1048576}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_plesk_account_usage() {
|
||||||
|
local username="$1"
|
||||||
|
local vhost_path="/var/www/vhosts/$username"
|
||||||
|
if [ -d "$vhost_path" ]; then
|
||||||
|
du -sb "$vhost_path" 2>/dev/null | awk '{printf "%.0f", $1/1048576}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_interworx_account_usage() {
|
||||||
|
local username="$1"
|
||||||
|
local home="/home/$username"
|
||||||
|
if [ -d "$home" ]; then
|
||||||
|
du -sb "$home" 2>/dev/null | awk '{printf "%.0f", $1/1048576}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_system_account_usage() {
|
||||||
|
local username="$1"
|
||||||
|
local home
|
||||||
|
home=$(getent passwd "$username" | cut -d: -f6)
|
||||||
|
if [ -n "$home" ] && [ -d "$home" ]; then
|
||||||
|
du -sb "$home" 2>/dev/null | awk '{printf "%.0f", $1/1048576}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get peak concurrent requests for a domain
|
||||||
|
get_domain_peak_concurrent() {
|
||||||
|
local domain="$1"
|
||||||
|
[ -z "$domain" ] && return 1
|
||||||
|
|
||||||
|
local log_file
|
||||||
|
log_file=$(find_domain_access_log "$domain")
|
||||||
|
|
||||||
|
if [ -z "$log_file" ] || [ ! -f "$log_file" ]; then
|
||||||
|
echo "0"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Analyze access log for peak concurrent requests (simplified)
|
||||||
|
tail -100000 "$log_file" 2>/dev/null | \
|
||||||
|
awk '{print $4}' | \
|
||||||
|
sed 's/\[//' | \
|
||||||
|
awk -F: '{print $3}' | \
|
||||||
|
sort | uniq -c | \
|
||||||
|
sort -rn | head -1 | \
|
||||||
|
awk '{print $1}' || echo "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if a domain is already optimized
|
||||||
|
is_domain_optimized() {
|
||||||
|
local domain="$1"
|
||||||
|
[ -z "$domain" ] && return 1
|
||||||
|
|
||||||
|
# Check if pool has been recently optimized (within last 7 days)
|
||||||
|
local pool_path
|
||||||
|
pool_path=$(php_detector_get_pool_config "$domain")
|
||||||
|
|
||||||
|
if [ -z "$pool_path" ] || [ ! -f "$pool_path" ]; then
|
||||||
|
echo "0"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if pm.max_children is set to something other than default (40)
|
||||||
|
local current_max
|
||||||
|
current_max=$(grep -oP 'pm\.max_children\s*=\s*\K\d+' "$pool_path" 2>/dev/null || echo "40")
|
||||||
|
|
||||||
|
if [ "$current_max" != "40" ]; then
|
||||||
|
echo "1"
|
||||||
|
else
|
||||||
|
echo "0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find which user owns a domain
|
||||||
|
find_domain_owner() {
|
||||||
|
local domain="$1"
|
||||||
|
[ -z "$domain" ] && return 1
|
||||||
|
|
||||||
|
case "${SYS_CONTROL_PANEL:-unknown}" in
|
||||||
|
cpanel)
|
||||||
|
grep "^${domain}:" /etc/trueuserdomains 2>/dev/null | cut -d: -f2
|
||||||
|
;;
|
||||||
|
plesk)
|
||||||
|
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
|
||||||
|
mysql -Ns psa -e "SELECT u.login FROM domains d JOIN sys_users u ON d.id=u.domain_id WHERE d.name='$domain' LIMIT 1" 2>/dev/null
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
interworx)
|
||||||
|
grep -l "^${domain}$" /etc/httpd/conf.d/vhost_*.conf 2>/dev/null | \
|
||||||
|
xargs grep "SuexecUserGroup" 2>/dev/null | \
|
||||||
|
head -1 | awk '{print $2}'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find access log for a domain
|
||||||
|
find_domain_access_log() {
|
||||||
|
local domain="$1"
|
||||||
|
[ -z "$domain" ] && return 1
|
||||||
|
|
||||||
|
case "${SYS_CONTROL_PANEL:-unknown}" in
|
||||||
|
cpanel)
|
||||||
|
local owner
|
||||||
|
owner=$(find_domain_owner "$domain")
|
||||||
|
if [ -n "$owner" ]; then
|
||||||
|
find "/home/${owner}/public_html" -maxdepth 2 -name "access_log*" -type f 2>/dev/null | head -1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
plesk)
|
||||||
|
find "/var/www/vhosts/${domain}/statistics/logs" -name "access_log*" -type f 2>/dev/null | head -1
|
||||||
|
;;
|
||||||
|
interworx)
|
||||||
|
find "/home/*/public_html/${domain}" -name "access_log*" -type f 2>/dev/null | head -1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
find /var/log -name "*${domain}*access*log*" -type f 2>/dev/null | head -1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HELPER FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Get count of total accounts
|
||||||
|
get_total_account_count() {
|
||||||
|
enumerate_all_accounts | wc -l
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get count of total domains
|
||||||
|
get_total_domain_count() {
|
||||||
|
enumerate_all_domains | wc -l
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear enumeration cache
|
||||||
|
clear_enumeration_cache() {
|
||||||
|
rm -f /tmp/php-scanner-*-cache-* 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display enumeration progress (for use in larger operations)
|
||||||
|
show_enumeration_progress() {
|
||||||
|
local current="$1"
|
||||||
|
local total="$2"
|
||||||
|
|
||||||
|
if [ -z "$total" ] || [ "$total" -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local percent=$((current * 100 / total))
|
||||||
|
local filled=$((percent / 5))
|
||||||
|
local empty=$((20 - filled))
|
||||||
|
|
||||||
|
printf "Progress: [%-20s] %3d%% (%d/%d)\r" \
|
||||||
|
"$(printf '#%.0s' $(seq 1 $filled))$(printf ' %.0s' $(seq 1 $empty))" \
|
||||||
|
"$percent" "$current" "$total"
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f enumerate_all_accounts
|
||||||
|
export -f enumerate_user_domains
|
||||||
|
export -f enumerate_all_domains
|
||||||
|
export -f filter_accounts_by_name
|
||||||
|
export -f filter_accounts_by_threshold
|
||||||
|
export -f filter_domains_by_name
|
||||||
|
export -f filter_domains_by_traffic
|
||||||
|
export -f filter_domains_by_optimization_status
|
||||||
|
export -f get_domain_php_info
|
||||||
|
export -f get_account_disk_usage
|
||||||
|
export -f get_domain_peak_concurrent
|
||||||
|
export -f is_domain_optimized
|
||||||
|
export -f find_domain_owner
|
||||||
|
export -f find_domain_access_log
|
||||||
|
export -f get_total_account_count
|
||||||
|
export -f get_total_domain_count
|
||||||
|
export -f clear_enumeration_cache
|
||||||
|
export -f show_enumeration_progress
|
||||||
Executable
+541
@@ -0,0 +1,541 @@
|
|||||||
|
#!/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
|
||||||
Executable
+608
@@ -0,0 +1,608 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PHP-FPM UI Module
|
||||||
|
# Handles all user interface: menus, prompts, displays, formatting
|
||||||
|
# Part of PHP Optimizer - Phase 3 Refactoring
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# COLOR CODES & DISPLAY UTILITIES
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Define color codes (must be done first)
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
MAGENTA='\033[0;35m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
WHITE='\033[1;37m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Safe color echo function
|
||||||
|
cecho() {
|
||||||
|
echo -e "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print a separator line
|
||||||
|
print_separator() {
|
||||||
|
local char="${1:-─}"
|
||||||
|
cecho "${CYAN}$(printf '%0.s%s' {1..73} <<< "$char")${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print a visual section header
|
||||||
|
print_header() {
|
||||||
|
local title="$1"
|
||||||
|
echo ""
|
||||||
|
cecho "${CYAN}╔════════════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
printf "${CYAN}║${NC} %-71s ${CYAN}║${NC}\n" "${title}"
|
||||||
|
cecho "${CYAN}╚════════════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# BANNER DISPLAY
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
show_banner() {
|
||||||
|
clear
|
||||||
|
cecho "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
cecho "${CYAN}║${WHITE} PHP & SERVER PERFORMANCE OPTIMIZER ${CYAN}║${NC}"
|
||||||
|
cecho "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# MAIN MENU
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
show_main_menu() {
|
||||||
|
cecho "${WHITE}${BOLD}MAIN MENU${NC}"
|
||||||
|
print_separator
|
||||||
|
echo ""
|
||||||
|
cecho " ${GREEN}1${NC}) Analyze Single Domain"
|
||||||
|
cecho " ${GREEN}2${NC}) Analyze All Domains (Server-Wide)"
|
||||||
|
cecho " ${GREEN}3${NC}) Quick Health Check (All Domains)"
|
||||||
|
cecho " ${GREEN}4${NC}) Optimize Domain PHP Settings"
|
||||||
|
cecho " ${GREEN}5${NC}) Optimize Server-Wide PHP Settings"
|
||||||
|
cecho " ${GREEN}6${NC}) View OPcache Statistics"
|
||||||
|
cecho " ${GREEN}7${NC}) View PHP-FPM Process Stats"
|
||||||
|
cecho " ${GREEN}8${NC}) Check for Configuration Issues"
|
||||||
|
cecho " ${GREEN}9${NC}) Check Server Memory Capacity (OOM Risk)"
|
||||||
|
echo ""
|
||||||
|
cecho " ${YELLOW}b${NC}) Backup Current Configurations"
|
||||||
|
cecho " ${YELLOW}r${NC}) Restore from Backup"
|
||||||
|
echo ""
|
||||||
|
cecho " ${RED}0${NC}) Exit"
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get menu selection from user with validation
|
||||||
|
get_main_menu_choice() {
|
||||||
|
while true; do
|
||||||
|
read -p "Select option (0-9, b, r): " choice
|
||||||
|
|
||||||
|
if ! [[ "$choice" =~ ^([0-9]|[bBrR])$ ]]; then
|
||||||
|
echo ""
|
||||||
|
cecho "${RED}Invalid choice. Please enter 0-9, b, or r${NC}"
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${choice,,}" # Return lowercase
|
||||||
|
break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOMAIN SELECTION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Select a single domain from all available domains
|
||||||
|
select_domain() {
|
||||||
|
local action="${1:-analyze}"
|
||||||
|
|
||||||
|
cecho "${WHITE}${BOLD}SELECT DOMAIN${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Use php-scanner if available, otherwise use direct functions
|
||||||
|
local domains
|
||||||
|
local -A domain_to_user
|
||||||
|
|
||||||
|
if type enumerate_all_domains >/dev/null 2>&1; then
|
||||||
|
# Use new php-scanner module for enumeration
|
||||||
|
all_domains=$(enumerate_all_domains)
|
||||||
|
|
||||||
|
while IFS= read -r domain; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
|
local owner
|
||||||
|
owner=$(find_domain_owner "$domain")
|
||||||
|
[ -z "$owner" ] && owner="unknown"
|
||||||
|
|
||||||
|
domain_to_user["$domain"]="$owner"
|
||||||
|
done <<< "$all_domains"
|
||||||
|
else
|
||||||
|
# Fallback to direct enumeration using sourced functions
|
||||||
|
local users
|
||||||
|
users=$(list_all_users)
|
||||||
|
|
||||||
|
if [ -z "$users" ]; then
|
||||||
|
cecho "${RED}ERROR: No users found on system${NC}"
|
||||||
|
read -p "Press Enter to continue..."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -a domains_arr
|
||||||
|
while IFS= read -r username; do
|
||||||
|
local user_domains
|
||||||
|
user_domains=$(get_user_domains "$username")
|
||||||
|
|
||||||
|
while IFS= read -r domain; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
domains_arr+=("$domain")
|
||||||
|
domain_to_user["$domain"]="$username"
|
||||||
|
done <<< "$user_domains"
|
||||||
|
done <<< "$users"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Convert associative array keys to indexed array
|
||||||
|
declare -a domains_list
|
||||||
|
for domain in "${!domain_to_user[@]}"; do
|
||||||
|
domains_list+=("$domain")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sort domains alphabetically
|
||||||
|
IFS=$'\n' read -rd '' -a domains_list <<<"$(printf '%s\n' "${domains_list[@]}" | sort)"
|
||||||
|
|
||||||
|
if [ ${#domains_list[@]} -eq 0 ]; then
|
||||||
|
cecho "${RED}ERROR: No domains found on system${NC}"
|
||||||
|
read -p "Press Enter to continue..."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display numbered list
|
||||||
|
cecho "${CYAN}Available domains (${#domains_list[@]} total):${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local index=1
|
||||||
|
for domain in "${domains_list[@]}"; do
|
||||||
|
local username="${domain_to_user[$domain]}"
|
||||||
|
local php_version="unknown"
|
||||||
|
|
||||||
|
if type detect_php_version_for_domain >/dev/null 2>&1; then
|
||||||
|
php_version=$(detect_php_version_for_domain "$username" "$domain" 2>/dev/null || echo "unknown")
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} ${YELLOW}(${php_version})${NC}\n" "$index" "$domain"
|
||||||
|
index=$((index + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
|
||||||
|
# Validate domain selection with retry loop
|
||||||
|
while true; do
|
||||||
|
read -p "Select domain number (or 'q' to cancel): " selection
|
||||||
|
|
||||||
|
if [[ "$selection" == "q" || "$selection" == "Q" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt ${#domains_list[@]} ]; then
|
||||||
|
echo ""
|
||||||
|
cecho "${RED}Invalid selection. Please enter a number 1-${#domains_list[@]}${NC}"
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
break
|
||||||
|
done
|
||||||
|
|
||||||
|
# Return selected domain and username
|
||||||
|
local selected_domain="${domains_list[$((selection - 1))]}"
|
||||||
|
local selected_user="${domain_to_user[$selected_domain]}"
|
||||||
|
|
||||||
|
echo "$selected_domain|$selected_user"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Select multiple domains for batch operations
|
||||||
|
select_multiple_domains() {
|
||||||
|
local mode="${1:-all}" # all, pattern, filtered, user
|
||||||
|
|
||||||
|
cecho "${WHITE}${BOLD}SELECT DOMAINS (BATCH)${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
all)
|
||||||
|
cecho "${CYAN}Using ALL domains on server${NC}"
|
||||||
|
enumerate_all_domains
|
||||||
|
;;
|
||||||
|
pattern)
|
||||||
|
cecho "${CYAN}Filter by pattern (e.g., *.example.com):${NC}"
|
||||||
|
read -p "Enter pattern: " pattern
|
||||||
|
filter_domains_by_name "$pattern"
|
||||||
|
;;
|
||||||
|
user)
|
||||||
|
cecho "${CYAN}Filter by user/account:${NC}"
|
||||||
|
local users
|
||||||
|
users=$(enumerate_all_accounts)
|
||||||
|
|
||||||
|
local -a accounts_list
|
||||||
|
while IFS= read -r user; do
|
||||||
|
accounts_list+=("$user")
|
||||||
|
done <<< "$users"
|
||||||
|
|
||||||
|
local index=1
|
||||||
|
for user in "${accounts_list[@]}"; do
|
||||||
|
echo " $index) $user"
|
||||||
|
index=$((index + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
read -p "Select user number: " user_choice
|
||||||
|
if [[ "$user_choice" =~ ^[0-9]+$ ]] && [ "$user_choice" -ge 1 ] && [ "$user_choice" -le ${#accounts_list[@]} ]; then
|
||||||
|
enumerate_user_domains "${accounts_list[$((user_choice - 1))]}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
traffic)
|
||||||
|
cecho "${CYAN}Filter by minimum concurrent requests:${NC}"
|
||||||
|
read -p "Enter minimum concurrent requests (default: 100): " min_requests
|
||||||
|
min_requests=${min_requests:-100}
|
||||||
|
filter_domains_by_traffic "$min_requests" "above"
|
||||||
|
;;
|
||||||
|
needs_optimization)
|
||||||
|
cecho "${CYAN}Showing domains that need optimization...${NC}"
|
||||||
|
filter_domains_by_optimization_status "needs_optimization"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SELECTION MENUS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Show options for optimization selection
|
||||||
|
show_optimization_menu() {
|
||||||
|
echo ""
|
||||||
|
cecho "${WHITE}${BOLD}OPTIMIZATION OPTIONS${NC}"
|
||||||
|
print_separator
|
||||||
|
echo ""
|
||||||
|
cecho " ${GREEN}1${NC}) Adjust PM Mode (static/dynamic/ondemand)"
|
||||||
|
cecho " ${GREEN}2${NC}) Adjust pm.max_children"
|
||||||
|
cecho " ${GREEN}3${NC}) Adjust pm.min_spare_servers"
|
||||||
|
cecho " ${GREEN}4${NC}) Adjust pm.max_spare_servers"
|
||||||
|
cecho " ${GREEN}5${NC}) Apply All Recommendations"
|
||||||
|
echo ""
|
||||||
|
cecho " ${RED}0${NC}) Cancel"
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
}
|
||||||
|
|
||||||
|
get_optimization_choice() {
|
||||||
|
while true; do
|
||||||
|
read -p "Select option (0-5): " choice
|
||||||
|
|
||||||
|
if ! [[ "$choice" =~ ^[0-5]$ ]]; then
|
||||||
|
echo ""
|
||||||
|
cecho "${RED}Invalid choice. Please enter 0-5${NC}"
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$choice"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show apply options menu
|
||||||
|
show_apply_menu() {
|
||||||
|
echo ""
|
||||||
|
cecho "${WHITE}${BOLD}APPLY CHANGES${NC}"
|
||||||
|
print_separator
|
||||||
|
echo ""
|
||||||
|
cecho " ${GREEN}1${NC}) Apply changes now"
|
||||||
|
cecho " ${GREEN}2${NC}) Show dry-run preview"
|
||||||
|
cecho " ${GREEN}3${NC}) Save recommendation to file"
|
||||||
|
echo ""
|
||||||
|
cecho " ${RED}0${NC}) Discard changes"
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
}
|
||||||
|
|
||||||
|
get_apply_choice() {
|
||||||
|
while true; do
|
||||||
|
read -p "Select option (0-3): " choice
|
||||||
|
|
||||||
|
if ! [[ "$choice" =~ ^[0-3]$ ]]; then
|
||||||
|
echo ""
|
||||||
|
cecho "${RED}Invalid choice. Please enter 0-3${NC}"
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$choice"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# BACKUP/RESTORE MENUS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Show backup selection menu
|
||||||
|
show_backup_menu() {
|
||||||
|
local backup_dir="${1:-.}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cecho "${WHITE}${BOLD}BACKUP CONFIGURATIONS${NC}"
|
||||||
|
echo ""
|
||||||
|
cecho "${CYAN}Available backups:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local backups
|
||||||
|
backups=$(find "$backup_dir" -maxdepth 1 -name "php-config-*.tar.gz" -type f 2>/dev/null | sort -r)
|
||||||
|
|
||||||
|
if [ -z "$backups" ]; then
|
||||||
|
cecho "${YELLOW}No backups found${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local index=1
|
||||||
|
declare -a backup_files
|
||||||
|
while IFS= read -r backup_file; do
|
||||||
|
[ -z "$backup_file" ] && continue
|
||||||
|
backup_files+=("$backup_file")
|
||||||
|
|
||||||
|
local timestamp
|
||||||
|
timestamp=$(stat -f %Sm -t "%Y-%m-%d %H:%M:%S" "$backup_file" 2>/dev/null || stat -c %y "$backup_file" 2>/dev/null | cut -d' ' -f1-2)
|
||||||
|
|
||||||
|
printf " ${GREEN}%-3d${NC}) ${CYAN}%s${NC}\n" "$index" "$(basename "$backup_file") - $timestamp"
|
||||||
|
index=$((index + 1))
|
||||||
|
done <<< "$backups"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -p "Select backup number (or 'q' to cancel): " selection
|
||||||
|
|
||||||
|
if [[ "$selection" == "q" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt ${#backup_files[@]} ]; then
|
||||||
|
echo ""
|
||||||
|
cecho "${RED}Invalid selection. Please enter 1-${#backup_files[@]}${NC}"
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
break
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "${backup_files[$((selection - 1))]}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# RESULT DISPLAY FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Display domain analysis results with formatting
|
||||||
|
display_domain_analysis() {
|
||||||
|
local domain="$1"
|
||||||
|
local analysis_output="$2"
|
||||||
|
|
||||||
|
print_header "Analysis Results for $domain"
|
||||||
|
|
||||||
|
cecho "$analysis_output"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display optimization results
|
||||||
|
display_optimization_results() {
|
||||||
|
local domain="$1"
|
||||||
|
local old_settings="$2"
|
||||||
|
local new_settings="$3"
|
||||||
|
|
||||||
|
print_header "Optimization Results for $domain"
|
||||||
|
|
||||||
|
cecho "${CYAN}Current Settings:${NC}"
|
||||||
|
cecho "$old_settings" | sed 's/^/ /'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cecho "${GREEN}Recommended Settings:${NC}"
|
||||||
|
cecho "$new_settings" | sed 's/^/ /'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display comparison results (old vs new)
|
||||||
|
display_comparison() {
|
||||||
|
local title="$1"
|
||||||
|
local old_result="$2"
|
||||||
|
local new_result="$3"
|
||||||
|
|
||||||
|
print_header "$title"
|
||||||
|
|
||||||
|
cecho "${YELLOW}Legacy Algorithm:${NC}"
|
||||||
|
cecho "$old_result" | sed 's/^/ /'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cecho "${GREEN}Improved Algorithm:${NC}"
|
||||||
|
cecho "$new_result" | sed 's/^/ /'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display progress bar for long operations
|
||||||
|
display_progress() {
|
||||||
|
local current="$1"
|
||||||
|
local total="$2"
|
||||||
|
local label="${3:-Progress}"
|
||||||
|
|
||||||
|
if [ -z "$total" ] || [ "$total" -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local percent=$((current * 100 / total))
|
||||||
|
local filled=$((percent / 5))
|
||||||
|
local empty=$((20 - filled))
|
||||||
|
|
||||||
|
printf "${label}: [%-20s] %3d%% (%d/%d)\r" \
|
||||||
|
"$(printf '#%.0s' $(seq 1 $filled))$(printf ' %.0s' $(seq 1 $empty))" \
|
||||||
|
"$percent" "$current" "$total"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display a spinner for indeterminate progress
|
||||||
|
display_spinner() {
|
||||||
|
local message="$1"
|
||||||
|
local pid="$2"
|
||||||
|
|
||||||
|
local -a spinner=( '⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏' )
|
||||||
|
|
||||||
|
while kill -0 "$pid" 2>/dev/null; do
|
||||||
|
for frame in "${spinner[@]}"; do
|
||||||
|
printf "\r${message} ${frame}"
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\r${message} ✓\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CONFIRMATION DIALOGS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Ask user for yes/no confirmation (from common-functions.sh)
|
||||||
|
confirm() {
|
||||||
|
local prompt="${1:-Continue?}"
|
||||||
|
local response
|
||||||
|
|
||||||
|
cecho "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
read -p "$prompt (y/n): " response
|
||||||
|
cecho "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
|
||||||
|
[[ "$response" =~ ^[yY]([eE][sS])?$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Confirm operation with domain list preview
|
||||||
|
confirm_batch_operation() {
|
||||||
|
local action="$1"
|
||||||
|
local domain_list="$2"
|
||||||
|
local domain_count="${3:-1}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_separator
|
||||||
|
cecho "${YELLOW}${BOLD}WARNING: About to $action on $domain_count domain(s)${NC}"
|
||||||
|
print_separator
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cecho "${CYAN}Affected domains:${NC}"
|
||||||
|
echo "$domain_list" | sed 's/^/ /'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if ! confirm "Continue?"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ERROR & STATUS MESSAGES
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Display error message
|
||||||
|
show_error() {
|
||||||
|
local message="$1"
|
||||||
|
echo ""
|
||||||
|
cecho "${RED}${BOLD}ERROR:${NC} $message"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display warning message
|
||||||
|
show_warning() {
|
||||||
|
local message="$1"
|
||||||
|
echo ""
|
||||||
|
cecho "${YELLOW}${BOLD}WARNING:${NC} $message"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display success message
|
||||||
|
show_success() {
|
||||||
|
local message="$1"
|
||||||
|
echo ""
|
||||||
|
cecho "${GREEN}${BOLD}SUCCESS:${NC} $message"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display info message
|
||||||
|
show_info() {
|
||||||
|
local message="$1"
|
||||||
|
echo ""
|
||||||
|
cecho "${CYAN}${BOLD}INFO:${NC} $message"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# UTILITY DISPLAY FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Show a key-value pair nicely formatted
|
||||||
|
show_setting() {
|
||||||
|
local label="$1"
|
||||||
|
local value="$2"
|
||||||
|
local color="${3:-$CYAN}"
|
||||||
|
|
||||||
|
printf " ${color}%-30s${NC}: %s\n" "$label" "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show a list of items with numbering
|
||||||
|
show_numbered_list() {
|
||||||
|
local -a items=("$@")
|
||||||
|
local index=1
|
||||||
|
|
||||||
|
for item in "${items[@]}"; do
|
||||||
|
printf " ${GREEN}%-3d${NC}) %s\n" "$index" "$item"
|
||||||
|
index=$((index + 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# EXPORT ALL FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
export -f cecho
|
||||||
|
export -f print_separator
|
||||||
|
export -f print_header
|
||||||
|
export -f show_banner
|
||||||
|
export -f show_main_menu
|
||||||
|
export -f get_main_menu_choice
|
||||||
|
export -f select_domain
|
||||||
|
export -f select_multiple_domains
|
||||||
|
export -f show_optimization_menu
|
||||||
|
export -f get_optimization_choice
|
||||||
|
export -f show_apply_menu
|
||||||
|
export -f get_apply_choice
|
||||||
|
export -f show_backup_menu
|
||||||
|
export -f display_domain_analysis
|
||||||
|
export -f display_optimization_results
|
||||||
|
export -f display_comparison
|
||||||
|
export -f display_progress
|
||||||
|
export -f display_spinner
|
||||||
|
export -f confirm
|
||||||
|
export -f confirm_batch_operation
|
||||||
|
export -f show_error
|
||||||
|
export -f show_warning
|
||||||
|
export -f show_success
|
||||||
|
export -f show_info
|
||||||
|
export -f show_setting
|
||||||
|
export -f show_numbered_list
|
||||||
+273
@@ -0,0 +1,273 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PHP-FPM Batch Analyzer - One-Shot Diagnostic Script
|
||||||
|
# Analyzes all domains on server, shows current vs recommended max_children
|
||||||
|
# Shows memory impact and optimization opportunities
|
||||||
|
# Drop in, run once, then delete
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PHP_TOOLKIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../.. && pwd)"
|
||||||
|
|
||||||
|
# Source required libraries
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/common-functions.sh" 2>/dev/null || { echo "ERROR: common-functions.sh not found"; exit 1; }
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/system-detect.sh" 2>/dev/null || { echo "ERROR: system-detect.sh not found"; exit 1; }
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/user-manager.sh" 2>/dev/null || { echo "ERROR: user-manager.sh not found"; exit 1; }
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-detector.sh" 2>/dev/null || { echo "ERROR: php-detector.sh not found"; exit 1; }
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-analyzer.sh" 2>/dev/null || { echo "ERROR: php-analyzer.sh not found"; exit 1; }
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-calculator-improved.sh" 2>/dev/null || { echo "ERROR: php-calculator-improved.sh not found"; exit 1; }
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-scanner.sh" 2>/dev/null || { echo "ERROR: php-scanner.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
# Color codes
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
WHITE='\033[1;37m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
cecho() {
|
||||||
|
echo -e "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# INITIALIZATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
initialize_system_detection
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
cecho "${RED}ERROR: This script must be run as root${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# MAIN ANALYSIS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
cecho "${CYAN}╔════════════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
cecho "${CYAN}║${WHITE} PHP-FPM BATCH ANALYZER - DIAGNOSTIC REPORT ${CYAN}║${NC}"
|
||||||
|
cecho "${CYAN}╚════════════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Get server info
|
||||||
|
cecho "${WHITE}${BOLD}SERVER INFORMATION${NC}"
|
||||||
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
TOTAL_RAM_MB=$(free -m | awk '/^Mem:/ {print $2}')
|
||||||
|
CPU_CORES=$(nproc)
|
||||||
|
CONTROL_PANEL="$SYS_CONTROL_PANEL"
|
||||||
|
|
||||||
|
cecho " Total RAM: ${WHITE}${TOTAL_RAM_MB}MB${NC}"
|
||||||
|
cecho " CPU Cores: ${WHITE}${CPU_CORES}${NC}"
|
||||||
|
cecho " Control Panel: ${WHITE}${CONTROL_PANEL}${NC}"
|
||||||
|
cecho " Scan Date: ${WHITE}$(date)${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOMAIN ENUMERATION & ANALYSIS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
cecho "${WHITE}${BOLD}DOMAIN-BY-DOMAIN ANALYSIS${NC}"
|
||||||
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Get all users and domains
|
||||||
|
users=$(list_all_users)
|
||||||
|
|
||||||
|
# Initialize tracking arrays
|
||||||
|
declare -a domain_list
|
||||||
|
declare -a domain_owner
|
||||||
|
declare -a current_max_children
|
||||||
|
declare -a recommended_max_children
|
||||||
|
declare -a memory_impact
|
||||||
|
declare -a needs_optimization
|
||||||
|
|
||||||
|
TOTAL_DOMAINS=0
|
||||||
|
TOTAL_CURRENT_MEMORY=0
|
||||||
|
TOTAL_RECOMMENDED_MEMORY=0
|
||||||
|
|
||||||
|
while IFS= read -r username; do
|
||||||
|
[ -z "$username" ] && continue
|
||||||
|
|
||||||
|
user_domains=$(get_user_domains "$username")
|
||||||
|
|
||||||
|
while IFS= read -r domain; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
|
TOTAL_DOMAINS=$((TOTAL_DOMAINS + 1))
|
||||||
|
domain_list[$TOTAL_DOMAINS]="$domain"
|
||||||
|
domain_owner[$TOTAL_DOMAINS]="$username"
|
||||||
|
|
||||||
|
# Find pool config
|
||||||
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
||||||
|
current_max_children[$TOTAL_DOMAINS]="ERROR"
|
||||||
|
recommended_max_children[$TOTAL_DOMAINS]="ERROR"
|
||||||
|
memory_impact[$TOTAL_DOMAINS]="?"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get current max_children
|
||||||
|
current=$(grep "^pm.max_children" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
||||||
|
current=${current:-40}
|
||||||
|
current_max_children[$TOTAL_DOMAINS]="$current"
|
||||||
|
|
||||||
|
# Calculate recommended using improved algorithm
|
||||||
|
recommended_result=$(calculate_optimal_php_settings "$username" "$TOTAL_RAM_MB" 2>/dev/null || echo "20||")
|
||||||
|
recommended=$(echo "$recommended_result" | cut -d'|' -f1)
|
||||||
|
recommended=${recommended:-20}
|
||||||
|
recommended_max_children[$TOTAL_DOMAINS]="$recommended"
|
||||||
|
|
||||||
|
# Calculate memory impact (assuming 20MB per process on average)
|
||||||
|
current_memory=$((current * 20))
|
||||||
|
recommended_memory=$((recommended * 20))
|
||||||
|
impact=$((current_memory - recommended_memory))
|
||||||
|
memory_impact[$TOTAL_DOMAINS]="$impact"
|
||||||
|
|
||||||
|
# Track totals
|
||||||
|
TOTAL_CURRENT_MEMORY=$((TOTAL_CURRENT_MEMORY + current_memory))
|
||||||
|
TOTAL_RECOMMENDED_MEMORY=$((TOTAL_RECOMMENDED_MEMORY + recommended_memory))
|
||||||
|
|
||||||
|
# Determine if optimization needed
|
||||||
|
if [ "$recommended" -lt "$current" ]; then
|
||||||
|
needs_optimization[$TOTAL_DOMAINS]="YES"
|
||||||
|
else
|
||||||
|
needs_optimization[$TOTAL_DOMAINS]="NO"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done <<< "$user_domains"
|
||||||
|
done <<< "$users"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DISPLAY RESULTS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Sort and display domains
|
||||||
|
OPTIMIZATION_COUNT=0
|
||||||
|
for idx in $(seq 1 $TOTAL_DOMAINS); do
|
||||||
|
domain="${domain_list[$idx]}"
|
||||||
|
owner="${domain_owner[$idx]}"
|
||||||
|
current="${current_max_children[$idx]}"
|
||||||
|
recommended="${recommended_max_children[$idx]}"
|
||||||
|
impact="${memory_impact[$idx]}"
|
||||||
|
optimize="${needs_optimization[$idx]}"
|
||||||
|
|
||||||
|
if [ "$current" == "ERROR" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format output
|
||||||
|
if [ "$optimize" == "YES" ]; then
|
||||||
|
cecho "${YELLOW}[$idx]${NC} $domain"
|
||||||
|
cecho " Owner: $owner"
|
||||||
|
cecho " Current max_children: ${RED}$current${NC} → Recommended: ${GREEN}$recommended${NC}"
|
||||||
|
cecho " Memory impact: ${GREEN}+${impact}MB${NC} if optimized"
|
||||||
|
cecho " Status: ${YELLOW}NEEDS OPTIMIZATION${NC}"
|
||||||
|
OPTIMIZATION_COUNT=$((OPTIMIZATION_COUNT + 1))
|
||||||
|
else
|
||||||
|
cecho "${GREEN}[$idx]${NC} $domain"
|
||||||
|
cecho " Owner: $owner"
|
||||||
|
cecho " max_children: $current (already optimized)"
|
||||||
|
cecho " Status: ${GREEN}OK${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SERVER-WIDE SUMMARY
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cecho "${WHITE}${BOLD}SERVER-WIDE SUMMARY${NC}"
|
||||||
|
cecho "${CYAN}═════════════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Calculate percentages
|
||||||
|
CURRENT_PERCENT=$((TOTAL_CURRENT_MEMORY * 100 / TOTAL_RAM_MB))
|
||||||
|
RECOMMENDED_PERCENT=$((TOTAL_RECOMMENDED_MEMORY * 100 / TOTAL_RAM_MB))
|
||||||
|
POTENTIAL_SAVINGS=$((TOTAL_CURRENT_MEMORY - TOTAL_RECOMMENDED_MEMORY))
|
||||||
|
POTENTIAL_SAVINGS_PERCENT=$((POTENTIAL_SAVINGS * 100 / TOTAL_CURRENT_MEMORY))
|
||||||
|
|
||||||
|
cecho " Total domains analyzed: ${WHITE}$TOTAL_DOMAINS${NC}"
|
||||||
|
cecho " Domains needing optimization: ${YELLOW}$OPTIMIZATION_COUNT${NC}"
|
||||||
|
cecho " Domains already optimized: ${GREEN}$((TOTAL_DOMAINS - OPTIMIZATION_COUNT))${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cecho " ${BOLD}Current Memory Allocation:${NC}"
|
||||||
|
cecho " Total: ${WHITE}${TOTAL_CURRENT_MEMORY}MB${NC} (${RED}${CURRENT_PERCENT}%${NC} of ${TOTAL_RAM_MB}MB RAM)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cecho " ${BOLD}Recommended Memory Allocation:${NC}"
|
||||||
|
cecho " Total: ${WHITE}${TOTAL_RECOMMENDED_MEMORY}MB${NC} (${GREEN}${RECOMMENDED_PERCENT}%${NC} of ${TOTAL_RAM_MB}MB RAM)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cecho " ${BOLD}Optimization Potential:${NC}"
|
||||||
|
cecho " Memory that could be freed: ${GREEN}${POTENTIAL_SAVINGS}MB${NC} (${POTENTIAL_SAVINGS_PERCENT}% reduction)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$OPTIMIZATION_COUNT" -gt 0 ]; then
|
||||||
|
cecho " ${BOLD}Recommendation:${NC}"
|
||||||
|
cecho " ${YELLOW}⚠ $OPTIMIZATION_COUNT domain(s) could be optimized${NC}"
|
||||||
|
cecho " Run: ${WHITE}php-optimizer.sh${NC} → ${CYAN}Option 5${NC} (Optimize Server-Wide)"
|
||||||
|
else
|
||||||
|
cecho " ${BOLD}Status:${NC}"
|
||||||
|
cecho " ${GREEN}✓ All domains are already optimized${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cecho "${CYAN}═════════════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SAFETY WARNINGS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Check memory headroom
|
||||||
|
AVAILABLE_AFTER_RECOMMENDED=$((TOTAL_RAM_MB - TOTAL_RECOMMENDED_MEMORY))
|
||||||
|
if [ "$AVAILABLE_AFTER_RECOMMENDED" -lt 2048 ]; then
|
||||||
|
cecho "${RED}${BOLD}⚠ WARNING: Limited memory headroom${NC}"
|
||||||
|
cecho " After applying recommended settings, only ${AVAILABLE_AFTER_RECOMMENDED}MB would be available"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if already optimized
|
||||||
|
if [ "$OPTIMIZATION_COUNT" -eq 0 ]; then
|
||||||
|
cecho "${GREEN}${BOLD}✓ All domains are already optimized${NC}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CLEANUP
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
cecho "${WHITE}${BOLD}Report complete${NC}"
|
||||||
|
cecho " Generated: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Optional: save to file
|
||||||
|
REPORT_FILE="/tmp/php-fpm-analysis-$(date +%Y%m%d-%H%M%S).txt"
|
||||||
|
if [ -w /tmp ]; then
|
||||||
|
{
|
||||||
|
echo "PHP-FPM BATCH ANALYSIS REPORT"
|
||||||
|
echo "Generated: $(date)"
|
||||||
|
echo ""
|
||||||
|
echo "SERVER INFORMATION"
|
||||||
|
echo "Total RAM: ${TOTAL_RAM_MB}MB"
|
||||||
|
echo "CPU Cores: ${CPU_CORES}"
|
||||||
|
echo "Control Panel: ${CONTROL_PANEL}"
|
||||||
|
echo ""
|
||||||
|
echo "SUMMARY"
|
||||||
|
echo "Total domains: $TOTAL_DOMAINS"
|
||||||
|
echo "Domains needing optimization: $OPTIMIZATION_COUNT"
|
||||||
|
echo "Current memory allocation: ${TOTAL_CURRENT_MEMORY}MB (${CURRENT_PERCENT}%)"
|
||||||
|
echo "Recommended memory allocation: ${TOTAL_RECOMMENDED_MEMORY}MB (${RECOMMENDED_PERCENT}%)"
|
||||||
|
echo "Potential savings: ${POTENTIAL_SAVINGS}MB (${POTENTIAL_SAVINGS_PERCENT}%)"
|
||||||
|
} > "$REPORT_FILE"
|
||||||
|
|
||||||
|
cecho "Report saved to: ${CYAN}$REPORT_FILE${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
@@ -13,6 +13,12 @@ source "$PHP_TOOLKIT_DIR/lib/php-analyzer.sh" || { echo "ERROR: php-analyzer.sh
|
|||||||
source "$PHP_TOOLKIT_DIR/lib/php-config-manager.sh" || { echo "ERROR: php-config-manager.sh not found"; exit 1; }
|
source "$PHP_TOOLKIT_DIR/lib/php-config-manager.sh" || { echo "ERROR: php-config-manager.sh not found"; exit 1; }
|
||||||
source "$PHP_TOOLKIT_DIR/lib/php-calculator-improved.sh" || { echo "ERROR: php-calculator-improved.sh not found"; exit 1; }
|
source "$PHP_TOOLKIT_DIR/lib/php-calculator-improved.sh" || { echo "ERROR: php-calculator-improved.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
# Phase 3 Modular Architecture - NEW (optional but recommended for batch operations)
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-ui.sh" 2>/dev/null || true
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-scanner.sh" 2>/dev/null || true
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-action-executor.sh" 2>/dev/null || true
|
||||||
|
source "$PHP_TOOLKIT_DIR/lib/php-server-manager.sh" 2>/dev/null || true
|
||||||
|
|
||||||
# Color codes (using safe echo -e)
|
# Color codes (using safe echo -e)
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
|
|||||||
Executable
+475
@@ -0,0 +1,475 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PHP Optimizer Phase 3 - Comprehensive Test Suite
|
||||||
|
# Tests all refactored modules for functionality and compatibility
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PHP_TOOLKIT_DIR="/root/server-toolkit"
|
||||||
|
TEST_RESULTS_FILE="/tmp/php-optimizer-phase3-test-results.txt"
|
||||||
|
TEST_PASSED=0
|
||||||
|
TEST_FAILED=0
|
||||||
|
|
||||||
|
# Color codes
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# Test result functions
|
||||||
|
test_pass() {
|
||||||
|
local test_name="$1"
|
||||||
|
echo -e "${GREEN}✓ PASS${NC}: $test_name" | tee -a "$TEST_RESULTS_FILE"
|
||||||
|
TEST_PASSED=$((TEST_PASSED + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
test_fail() {
|
||||||
|
local test_name="$1"
|
||||||
|
local reason="$2"
|
||||||
|
echo -e "${RED}✗ FAIL${NC}: $test_name" | tee -a "$TEST_RESULTS_FILE"
|
||||||
|
[ -n "$reason" ] && echo " Reason: $reason" | tee -a "$TEST_RESULTS_FILE"
|
||||||
|
TEST_FAILED=$((TEST_FAILED + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
test_skip() {
|
||||||
|
local test_name="$1"
|
||||||
|
echo -e "${YELLOW}⊘ SKIP${NC}: $test_name" | tee -a "$TEST_RESULTS_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHASE 3c STEP 1: MODULE LOADING TEST
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c STEP 1: MODULE LOADING TEST ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
> "$TEST_RESULTS_FILE"
|
||||||
|
|
||||||
|
# Test 1.1: Source php-ui.sh
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-ui.sh 2>/dev/null
|
||||||
|
[ $(type -t show_banner | wc -l) -gt 0 ] && exit 0 || exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "php-ui.sh loads without errors"
|
||||||
|
else
|
||||||
|
test_fail "php-ui.sh loads without errors" "Module failed to load"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 1.2: Source php-scanner.sh
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
[ $(type -t enumerate_all_accounts | wc -l) -gt 0 ] && exit 0 || exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "php-scanner.sh loads without errors"
|
||||||
|
else
|
||||||
|
test_fail "php-scanner.sh loads without errors" "Module failed to load"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 1.3: Source php-action-executor.sh
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
[ $(type -t init_change_tracking | wc -l) -gt 0 ] && exit 0 || exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "php-action-executor.sh loads without errors"
|
||||||
|
else
|
||||||
|
test_fail "php-action-executor.sh loads without errors" "Module failed to load"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 1.4: Source php-server-manager.sh
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-analyzer.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-server-manager.sh 2>/dev/null
|
||||||
|
[ $(type -t scan_entire_server | wc -l) -gt 0 ] && exit 0 || exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "php-server-manager.sh loads without errors"
|
||||||
|
else
|
||||||
|
test_fail "php-server-manager.sh loads without errors" "Module failed to load"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 1.5: All modules together
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-detector.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-analyzer.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-config-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-calculator-improved.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-ui.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-server-manager.sh 2>/dev/null
|
||||||
|
|
||||||
|
# Verify key functions from each module
|
||||||
|
type show_banner >/dev/null 2>&1 || exit 1
|
||||||
|
type enumerate_all_domains >/dev/null 2>&1 || exit 1
|
||||||
|
type apply_optimization >/dev/null 2>&1 || exit 1
|
||||||
|
type execute_server_optimization_plan >/dev/null 2>&1 || exit 1
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "All modules load together without conflicts"
|
||||||
|
else
|
||||||
|
test_fail "All modules load together without conflicts" "Conflicts or missing functions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHASE 3c STEP 2: CONTROL PANEL ENUMERATION TEST
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c STEP 2: CONTROL PANEL ENUMERATION TEST ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 2.1: List all accounts
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
|
||||||
|
initialize_system_detection
|
||||||
|
accounts=$(enumerate_all_accounts)
|
||||||
|
[ -n "$accounts" ] && [ $(echo "$accounts" | wc -l) -gt 0 ] && exit 0 || exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
account_count=$(bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
initialize_system_detection
|
||||||
|
enumerate_all_accounts | wc -l
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
test_pass "enumerate_all_accounts() returns accounts ($account_count found)"
|
||||||
|
else
|
||||||
|
test_fail "enumerate_all_accounts() returns accounts" "No accounts enumerated"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2.2: List domains for first account
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
|
||||||
|
initialize_system_detection
|
||||||
|
first_account=$(enumerate_all_accounts | head -1)
|
||||||
|
[ -z "$first_account" ] && exit 1
|
||||||
|
|
||||||
|
domains=$(enumerate_user_domains "$first_account" 2>/dev/null)
|
||||||
|
# Domains may or may not exist, but function should work
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "enumerate_user_domains() works for first account"
|
||||||
|
else
|
||||||
|
test_fail "enumerate_user_domains() works for first account" "Function failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2.3: enumerate_all_domains (server-wide)
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
|
||||||
|
initialize_system_detection
|
||||||
|
all_domains=$(enumerate_all_domains)
|
||||||
|
# Function should return something (or empty if no domains)
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "enumerate_all_domains() completes without error"
|
||||||
|
else
|
||||||
|
test_fail "enumerate_all_domains() completes without error" "Function failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHASE 3c STEP 3: FILTERING AND SELECTION TEST
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c STEP 3: FILTERING AND SELECTION TEST ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 3.1: Account name filtering
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
|
||||||
|
initialize_system_detection
|
||||||
|
# Try filtering with a pattern (may return nothing, but function should work)
|
||||||
|
filtered=$(filter_accounts_by_name "a" 2>/dev/null)
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "filter_accounts_by_name() executes without error"
|
||||||
|
else
|
||||||
|
test_fail "filter_accounts_by_name() executes without error" "Function failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3.2: Domain name filtering
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/system-detect.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/user-manager.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-scanner.sh 2>/dev/null
|
||||||
|
|
||||||
|
initialize_system_detection
|
||||||
|
filtered=$(filter_domains_by_name "." 2>/dev/null)
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "filter_domains_by_name() executes without error"
|
||||||
|
else
|
||||||
|
test_fail "filter_domains_by_name() executes without error" "Function failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3.3: Menu functions
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-ui.sh 2>/dev/null
|
||||||
|
|
||||||
|
# Test that menu functions exist
|
||||||
|
type show_main_menu >/dev/null 2>&1 || exit 1
|
||||||
|
type show_optimization_menu >/dev/null 2>&1 || exit 1
|
||||||
|
type show_apply_menu >/dev/null 2>&1 || exit 1
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "Menu functions available and callable"
|
||||||
|
else
|
||||||
|
test_fail "Menu functions available and callable" "Functions missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHASE 3c STEP 4: BATCH OPERATIONS AND ROLLBACK TEST
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c STEP 4: BATCH OPERATIONS AND ROLLBACK TEST ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 4.1: Change tracking
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
|
||||||
|
init_change_tracking "test-session-$$"
|
||||||
|
[ -n "$EXECUTOR_SESSION_ID" ] && [ -n "$EXECUTOR_CHANGE_LOG" ] && exit 0 || exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "init_change_tracking() initializes session"
|
||||||
|
else
|
||||||
|
test_fail "init_change_tracking() initializes session" "Initialization failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4.2: Backup functionality
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
|
||||||
|
# This should fail gracefully if config not found (expected)
|
||||||
|
backup_domain_config "test.example.com" "testuser" 2>/dev/null
|
||||||
|
# Function should exist and be callable
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "backup_domain_config() is callable"
|
||||||
|
else
|
||||||
|
test_fail "backup_domain_config() is callable" "Function error"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4.3: Verification functions
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/common-functions.sh 2>/dev/null
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
|
||||||
|
type verify_applied_changes >/dev/null 2>&1 || exit 1
|
||||||
|
type validate_pool_config >/dev/null 2>&1 || exit 1
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "Verification functions available"
|
||||||
|
else
|
||||||
|
test_fail "Verification functions available" "Functions missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4.4: PHP-FPM service functions
|
||||||
|
bash << 'EOF'
|
||||||
|
source /root/server-toolkit/lib/php-action-executor.sh 2>/dev/null
|
||||||
|
|
||||||
|
type reload_php_fpm >/dev/null 2>&1 || exit 1
|
||||||
|
type restart_php_fpm >/dev/null 2>&1 || exit 1
|
||||||
|
type get_php_fpm_status >/dev/null 2>&1 || exit 1
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "PHP-FPM service functions available"
|
||||||
|
else
|
||||||
|
test_fail "PHP-FPM service functions available" "Functions missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHASE 3c STEP 5: BACKWARD COMPATIBILITY TEST
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c STEP 5: BACKWARD COMPATIBILITY TEST ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 5.1: Original php-optimizer.sh still works
|
||||||
|
bash -n /root/server-toolkit/modules/performance/php-optimizer.sh
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "php-optimizer.sh passes syntax check"
|
||||||
|
else
|
||||||
|
test_fail "php-optimizer.sh passes syntax check" "Syntax error"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5.2: Original functions still referenced
|
||||||
|
grep -q "analyze_single_domain" /root/server-toolkit/modules/performance/php-optimizer.sh
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "Original function names still in php-optimizer.sh"
|
||||||
|
else
|
||||||
|
test_fail "Original function names still in php-optimizer.sh" "Functions removed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5.3: Color codes preserved
|
||||||
|
grep -q "RED=" /root/server-toolkit/modules/performance/php-optimizer.sh
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "Color code definitions preserved"
|
||||||
|
else
|
||||||
|
test_fail "Color code definitions preserved" "Color codes missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5.4: Menu structure intact
|
||||||
|
grep -q "show_main_menu" /root/server-toolkit/modules/performance/php-optimizer.sh
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
test_pass "Menu display functions referenced"
|
||||||
|
else
|
||||||
|
test_fail "Menu display functions referenced" "Menu functions missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PHASE 3c STEP 6: PERFORMANCE AND STRESS TEST
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c STEP 6: PERFORMANCE AND STRESS TEST ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 6.1: Module size reasonable
|
||||||
|
UI_SIZE=$(wc -l < /root/server-toolkit/lib/php-ui.sh)
|
||||||
|
if [ "$UI_SIZE" -gt 500 ] && [ "$UI_SIZE" -lt 800 ]; then
|
||||||
|
test_pass "php-ui.sh size is reasonable ($UI_SIZE lines)"
|
||||||
|
else
|
||||||
|
test_fail "php-ui.sh size is reasonable" "Size: $UI_SIZE (expected 500-800)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6.2: php-scanner.sh size reasonable
|
||||||
|
SCANNER_SIZE=$(wc -l < /root/server-toolkit/lib/php-scanner.sh)
|
||||||
|
if [ "$SCANNER_SIZE" -gt 500 ] && [ "$SCANNER_SIZE" -lt 600 ]; then
|
||||||
|
test_pass "php-scanner.sh size is reasonable ($SCANNER_SIZE lines)"
|
||||||
|
else
|
||||||
|
test_fail "php-scanner.sh size is reasonable" "Size: $SCANNER_SIZE (expected 500-600)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6.3: php-action-executor.sh size reasonable
|
||||||
|
EXECUTOR_SIZE=$(wc -l < /root/server-toolkit/lib/php-action-executor.sh)
|
||||||
|
if [ "$EXECUTOR_SIZE" -gt 550 ] && [ "$EXECUTOR_SIZE" -lt 650 ]; then
|
||||||
|
test_pass "php-action-executor.sh size is reasonable ($EXECUTOR_SIZE lines)"
|
||||||
|
else
|
||||||
|
test_fail "php-action-executor.sh size is reasonable" "Size: $EXECUTOR_SIZE (expected 550-650)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6.4: php-server-manager.sh size reasonable
|
||||||
|
MANAGER_SIZE=$(wc -l < /root/server-toolkit/lib/php-server-manager.sh)
|
||||||
|
if [ "$MANAGER_SIZE" -gt 500 ] && [ "$MANAGER_SIZE" -lt 600 ]; then
|
||||||
|
test_pass "php-server-manager.sh size is reasonable ($MANAGER_SIZE lines)"
|
||||||
|
else
|
||||||
|
test_fail "php-server-manager.sh size is reasonable" "Size: $MANAGER_SIZE (expected 500-600)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6.5: All modules available
|
||||||
|
if [ -f /root/server-toolkit/lib/php-ui.sh ] && \
|
||||||
|
[ -f /root/server-toolkit/lib/php-scanner.sh ] && \
|
||||||
|
[ -f /root/server-toolkit/lib/php-action-executor.sh ] && \
|
||||||
|
[ -f /root/server-toolkit/lib/php-server-manager.sh ]; then
|
||||||
|
test_pass "All module files exist and are readable"
|
||||||
|
else
|
||||||
|
test_fail "All module files exist and are readable" "One or more files missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# TEST SUMMARY
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ PHASE 3c TEST SUMMARY ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
TOTAL=$((TEST_PASSED + TEST_FAILED))
|
||||||
|
|
||||||
|
echo "Results: $TOTAL tests executed"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Passed: $TEST_PASSED${NC}"
|
||||||
|
echo -e "${RED}Failed: $TEST_FAILED${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $TEST_FAILED -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}✓ ALL TESTS PASSED${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ SOME TESTS FAILED${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user