Compare commits

...

3 Commits

Author SHA1 Message Date
cschantz 03221476cd Implement batch operations to select and optimize multiple domains at once
- Add batch operation option to Option 4
- Allow user to select single domain or multiple domains
- Display optimization status [NEEDS OPTIMIZATION] or [OK] for each domain
- Support 'all' selection or individual number selection
- Optimizes selected domains in sequence
- Shows progress and summary of batch operation
- Includes simplified per-domain optimization for batch mode
- Provides fallback if recommendations can't be calculated

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-17 23:06:08 -05:00
cschantz a0bdedfbaf Add config validation and post-restart verification to Option 4
- Validate pool configuration after changes applied
- Automatic rollback if config validation fails
- Verify PHP-FPM restarted successfully and is accepting connections
- Verify new configuration actually loaded into memory
- Automatic rollback if PHP-FPM doesn't start after changes
- Provides safety checks to prevent broken configurations
- Better error handling and recovery options

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-17 23:05:32 -05:00
cschantz b3c60f9c1e Implement PM mode optimization (STATIC/DYNAMIC/ONDEMAND) for Option 4
- Add apply_pm_mode selection logic
- Display PM mode as separate option (option 2) in optimization menu
- Apply pm, pm.min_spare_servers, and pm.max_spare_servers settings
- Uses improved algorithm recommendations for DYNAMIC/ONDEMAND modes
- Includes min_spare and max_spare configuration for non-STATIC modes
- Now applies full set of recommendations from calculator, not just max_children

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-17 23:05:12 -05:00
+329 -11
View File
@@ -309,6 +309,37 @@ quick_health_check() {
optimize_domain() { optimize_domain() {
show_banner show_banner
cecho "${WHITE}${BOLD}DOMAIN OPTIMIZATION${NC}"
echo ""
cecho " ${GREEN}s${NC}) Optimize single domain"
cecho " ${GREEN}m${NC}) Optimize multiple domains (batch)"
cecho " ${RED}q${NC}) Cancel"
echo ""
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
read -p "Select option: " batch_choice
case "${batch_choice,,}" in
s)
optimize_single_domain_wrapper
;;
m)
optimize_multiple_domains_wrapper
;;
q)
return
;;
*)
cecho "${RED}Invalid selection${NC}"
read -p "Press Enter to continue..."
return
;;
esac
}
optimize_single_domain_wrapper() {
show_banner
local selection local selection
selection=$(select_domain "optimize") selection=$(select_domain "optimize")
@@ -535,7 +566,8 @@ optimize_domain() {
echo "" echo ""
cecho " ${GREEN}a${NC}) Apply all $opt_count recommendations" cecho " ${GREEN}a${NC}) Apply all $opt_count recommendations"
[ "${opt_available[max_children]}" = "true" ] && cecho " ${GREEN}1${NC}) Apply max_children optimization only" [ "${opt_available[max_children]}" = "true" ] && cecho " ${GREEN}1${NC}) Apply max_children optimization only"
[ "${opt_available[opcache]}" = "true" ] && cecho " ${GREEN}2${NC}) Apply OPcache optimization only" [ "${opt_available[pm_mode]}" = "true" ] && cecho " ${GREEN}2${NC}) Apply PM mode optimization only"
[ "${opt_available[opcache]}" = "true" ] && cecho " ${GREEN}3${NC}) Apply OPcache optimization only"
cecho " ${RED}n${NC}) Cancel - don't apply any changes" cecho " ${RED}n${NC}) Cancel - don't apply any changes"
echo "" echo ""
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
@@ -544,9 +576,9 @@ optimize_domain() {
while true; do while true; do
read -p "Select option: " apply_choice read -p "Select option: " apply_choice
if ! [[ "$apply_choice" =~ ^[a1A2nN]$ ]]; then if ! [[ "$apply_choice" =~ ^[a123nNA]$ ]]; then
echo "" echo ""
cecho "${RED}Invalid selection. Please enter a, 1, 2, or n${NC}" cecho "${RED}Invalid selection. Please enter a, 1, 2, 3, or n${NC}"
echo "" echo ""
continue continue
fi fi
@@ -555,6 +587,7 @@ optimize_domain() {
# Determine which optimizations to apply # Determine which optimizations to apply
local apply_max_children=false local apply_max_children=false
local apply_pm_mode=false
local apply_opcache=false local apply_opcache=false
# Convert to lowercase for consistent case matching # Convert to lowercase for consistent case matching
@@ -563,12 +596,16 @@ optimize_domain() {
case "$apply_choice" in case "$apply_choice" in
a) a)
apply_max_children=${opt_available[max_children]:-false} apply_max_children=${opt_available[max_children]:-false}
apply_pm_mode=${opt_available[pm_mode]:-false}
apply_opcache=${opt_available[opcache]:-false} apply_opcache=${opt_available[opcache]:-false}
;; ;;
1) 1)
apply_max_children=${opt_available[max_children]:-false} apply_max_children=${opt_available[max_children]:-false}
;; ;;
2) 2)
apply_pm_mode=${opt_available[pm_mode]:-false}
;;
3)
apply_opcache=${opt_available[opcache]:-false} apply_opcache=${opt_available[opcache]:-false}
;; ;;
n) n)
@@ -580,7 +617,7 @@ optimize_domain() {
esac esac
# Check if nothing was selected # Check if nothing was selected
if [ "$apply_max_children" = "false" ] && [ "$apply_opcache" = "false" ]; then if [ "$apply_max_children" = "false" ] && [ "$apply_pm_mode" = "false" ] && [ "$apply_opcache" = "false" ]; then
cecho "${YELLOW}No optimizations selected${NC}" cecho "${YELLOW}No optimizations selected${NC}"
echo "" echo ""
read -p "Press Enter to continue..." read -p "Press Enter to continue..."
@@ -624,6 +661,34 @@ optimize_domain() {
fi fi
fi fi
# Apply PM mode if selected (STATIC/DYNAMIC/ONDEMAND)
if [ "$apply_pm_mode" = "true" ]; then
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
if [ -n "$improved_pm_mode" ]; then
# Apply pm mode
if modify_fpm_pool_setting "$pool_config" "pm" "$improved_pm_mode" >/dev/null 2>&1; then
cecho " ${GREEN}${NC} Set pm = $improved_pm_mode"
# For DYNAMIC and ONDEMAND modes, also set min/max spare servers
if [[ "$improved_pm_mode" == "dynamic" ]] || [[ "$improved_pm_mode" == "ondemand" ]]; then
if modify_fpm_pool_setting "$pool_config" "pm.min_spare_servers" "$improved_min_spare" >/dev/null 2>&1; then
cecho " ${GREEN}${NC} Set pm.min_spare_servers = $improved_min_spare"
fi
if modify_fpm_pool_setting "$pool_config" "pm.max_spare_servers" "$improved_max_spare" >/dev/null 2>&1; then
cecho " ${GREEN}${NC} Set pm.max_spare_servers = $improved_max_spare"
fi
fi
changes_made=$((changes_made + 1))
else
cecho " ${RED}${NC} Failed to set pm mode"
changes_failed=$((changes_failed + 1))
fi
fi
fi
fi
# Apply OPcache if selected # Apply OPcache if selected
if [ "$apply_opcache" = "true" ]; then if [ "$apply_opcache" = "true" ]; then
# Note: OPcache settings are in php.ini, not FPM pool config # Note: OPcache settings are in php.ini, not FPM pool config
@@ -636,6 +701,35 @@ optimize_domain() {
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
if [ "$changes_made" -gt 0 ]; then if [ "$changes_made" -gt 0 ]; then
# Validate the pool config before reloading
cecho "${CYAN}Validating configuration...${NC}"
local config_valid=true
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
if ! validate_pool_config "$pool_config" >/dev/null 2>&1; then
cecho "${RED}✗ Configuration validation failed!${NC}"
cecho "${YELLOW}The modified configuration has syntax errors.${NC}"
config_valid=false
# Offer to rollback
if confirm "Rollback changes?"; then
cecho "${CYAN}Rolling back configuration...${NC}"
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
cecho "${GREEN}✓ Configuration restored from backup${NC}"
else
cecho "${RED}✗ Rollback failed - manual recovery may be needed${NC}"
cecho "${YELLOW}Backup location: $backup_dir${NC}"
fi
echo ""
read -p "Press Enter to continue..."
return
fi
else
cecho "${GREEN}✓ Configuration is valid${NC}"
fi
fi
echo ""
cecho "${GREEN}${BOLD}✓ Applied $changes_made optimization(s)${NC}" cecho "${GREEN}${BOLD}✓ Applied $changes_made optimization(s)${NC}"
if [ "$changes_failed" -gt 0 ]; then if [ "$changes_failed" -gt 0 ]; then
@@ -654,20 +748,43 @@ optimize_domain() {
cecho "${CYAN}Restarting PHP-FPM ($php_version)...${NC}" cecho "${CYAN}Restarting PHP-FPM ($php_version)...${NC}"
if reload_php_fpm "$php_version" >/dev/null 2>&1; then if reload_php_fpm "$php_version" >/dev/null 2>&1; then
cecho "${GREEN}✓ PHP-FPM reloaded successfully${NC}" cecho "${GREEN}✓ PHP-FPM reloaded${NC}"
# Verify it's running # Verify it's running
sleep 1 sleep 2
if verify_php_fpm_running "$php_version" >/dev/null 2>&1; then if verify_php_fpm_running "$php_version" >/dev/null 2>&1; then
cecho "${GREEN}✓ PHP-FPM is running${NC}" cecho "${GREEN}✓ PHP-FPM is running and accepting connections${NC}"
# Verify applied changes - check if new config loaded
local verify_result
verify_result=$(verify_applied_changes "$username" "$domain" 2>/dev/null || echo "")
if [ -n "$verify_result" ]; then
cecho "${GREEN}✓ New configuration loaded successfully${NC}"
fi
else else
cecho "${RED}✗ WARNING: PHP-FPM may not be running!${NC}" cecho "${RED}✗ WARNING: PHP-FPM is not responding!${NC}"
cecho "${YELLOW}Run: systemctl status ea-php${php_version#ea-php}-php-fpm${NC}" cecho "${YELLOW}Service may have crashed due to invalid configuration.${NC}"
cecho "${YELLOW}Attempting rollback...${NC}"
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
cecho "${GREEN}✓ Configuration rolled back${NC}"
cecho "${YELLOW}Restart PHP-FPM manually once this script completes${NC}"
else
cecho "${RED}✗ Rollback failed - manual recovery needed${NC}"
cecho "${YELLOW}Backup location: $backup_dir${NC}"
fi
fi fi
else else
cecho "${RED}✗ Failed to restart PHP-FPM${NC}" cecho "${RED}✗ Failed to restart PHP-FPM${NC}"
cecho "${YELLOW}You may need to restart manually:${NC}" cecho "${YELLOW}Attempting rollback...${NC}"
cecho "${YELLOW}systemctl restart ea-php${php_version#ea-php}-php-fpm${NC}"
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
cecho "${GREEN}✓ Configuration rolled back to previous state${NC}"
else
cecho "${RED}✗ Rollback failed - manual recovery may be needed${NC}"
cecho "${YELLOW}Backup location: $backup_dir${NC}"
fi
fi fi
else else
cecho "${YELLOW}Skipping restart - remember to restart PHP-FPM manually!${NC}" cecho "${YELLOW}Skipping restart - remember to restart PHP-FPM manually!${NC}"
@@ -684,6 +801,207 @@ optimize_domain() {
read -p "Press Enter to continue..." read -p "Press Enter to continue..."
} }
optimize_multiple_domains_wrapper() {
show_banner
cecho "${WHITE}${BOLD}SELECT DOMAINS TO OPTIMIZE${NC}"
echo ""
cecho "${YELLOW}Batch operation: Optimize multiple domains in sequence${NC}"
echo ""
# Get all users with domains
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
fi
# Build domain list with optimization status
declare -a domains
declare -A domain_to_user
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+=("$domain")
domain_to_user["$domain"]="$username"
done <<< "$user_domains"
done <<< "$users"
if [ ${#domains[@]} -eq 0 ]; then
cecho "${RED}ERROR: No domains found on system${NC}"
read -p "Press Enter to continue..."
return
fi
# Display numbered list with optimization status
cecho "${CYAN}Available domains (select ones to optimize):${NC}"
echo ""
local index=1
for domain in "${domains[@]}"; do
local username="${domain_to_user[$domain]}"
local issues
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
local has_high_issues
has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
local status_indicator=""
if [ "$has_high_issues" -gt 0 ]; then
status_indicator="${YELLOW}[NEEDS OPTIMIZATION]${NC}"
else
status_indicator="${GREEN}[OK]${NC}"
fi
printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} %s\n" "$index" "$domain" "$(echo -e "$status_indicator")"
index=$((index + 1))
done
echo ""
cecho "${CYAN}Enter numbers separated by spaces (e.g., '1 3 5') or 'all' for all domains, 'none' to cancel${NC}"
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
read -p "Selection: " user_selection
# Parse selection
user_selection=$(echo "$user_selection" | tr '[:upper:]' '[:lower:]')
declare -a selected_domains
if [[ "$user_selection" == "all" ]]; then
selected_domains=("${domains[@]}")
elif [[ "$user_selection" =~ ^(none|n)$ ]]; then
return
else
# Parse individual numbers
for num in $user_selection; do
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#domains[@]} ]; then
selected_domains+=("${domains[$((num - 1))]}")
fi
done
fi
if [ ${#selected_domains[@]} -eq 0 ]; then
cecho "${YELLOW}No domains selected${NC}"
read -p "Press Enter to continue..."
return
fi
# Confirm
echo ""
cecho "${CYAN}Selected ${#selected_domains[@]} domain(s) for optimization${NC}"
if ! confirm "Continue?"; then
return
fi
# Optimize each selected domain
show_banner
cecho "${WHITE}${BOLD}BATCH OPTIMIZATION IN PROGRESS${NC}"
echo ""
local optimized=0
local failed=0
for domain in "${selected_domains[@]}"; do
local username="${domain_to_user[$domain]}"
cecho "${CYAN}Optimizing: ${WHITE}$domain${NC} ${CYAN}[$username]${NC}"
# Run single domain optimization (simplified)
optimize_domain_direct "$domain" "$username"
local result=$?
if [ $result -eq 0 ]; then
optimized=$((optimized + 1))
else
failed=$((failed + 1))
fi
echo ""
done
# Display summary
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
cecho "${WHITE}${BOLD}BATCH OPTIMIZATION COMPLETE${NC}"
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
cecho " Successfully optimized: ${GREEN}$optimized${NC}"
if [ "$failed" -gt 0 ]; then
cecho " Failed: ${RED}$failed${NC}"
fi
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
echo ""
read -p "Press Enter to continue..."
}
optimize_domain_direct() {
local domain="$1"
local username="$2"
# Detect issues
local issues
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "")
# Check if there are issues
local critical_high_count
critical_high_count=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
if [ "$critical_high_count" -eq 0 ]; then
cecho " ${GREEN}✓ No optimization needed${NC}"
return 0
fi
# Get pool config
local pool_config
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
cecho " ${YELLOW}⊘ No pool config found - skipping${NC}"
return 1
fi
# Get recommendations
local total_ram_mb
total_ram_mb=$(free -m | awk '/^Mem:/ {print $2}')
local improved_result
improved_result=$(calculate_optimal_php_settings "$username" "$total_ram_mb" 2>/dev/null || echo "")
if [ -z "$improved_result" ]; then
cecho " ${YELLOW}⊘ Could not calculate recommendations - skipping${NC}"
return 1
fi
local improved_max=$(get_field "$improved_result" 1)
local current_max=$(grep "^pm.max_children" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
if [ -z "$current_max" ] || [ -z "$improved_max" ] || [ "$current_max" = "$improved_max" ]; then
cecho " ${GREEN}✓ Already optimized${NC}"
return 0
fi
# Create backup
local backup_dir
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
if [ $? -ne 0 ]; then
cecho " ${RED}✗ Backup failed - skipping${NC}"
return 1
fi
# Apply optimization
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$improved_max" >/dev/null 2>&1; then
cecho " ${GREEN}✓ Updated max_children: $current_max$improved_max${NC}"
return 0
else
cecho " ${RED}✗ Optimization failed${NC}"
return 1
fi
}
# ============================================================================ # ============================================================================
# OPTIMIZE ALL DOMAINS (SERVER-WIDE) # OPTIMIZE ALL DOMAINS (SERVER-WIDE)
# ============================================================================ # ============================================================================