Last active
April 27, 2025 17:25
-
-
Save fabriziosalmi/d4e87e417b7cc22edaf0da11696dbd57 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Script to configure network interfaces for multiple gateways on Ubuntu 24.04+ | |
# ens18 (default): DHCP, default route for general traffic | |
# ens19 (secondary): Static IP, uses its own gateway ONLY for traffic originating from its IP. | |
# Enhanced with validation, rollback, policy routing, and improved error handling. | |
# Strict mode | |
set -u # Exit on unset variables | |
set -o pipefail # Exit on pipe failures | |
# --- Configuration Variables --- | |
NETPLAN_FILE="/etc/netplan/99-custom-netplan.yaml" | |
CUSTOM_ROUTE_TABLE_ID="200" | |
CUSTOM_ROUTING_SERVICE_NAME="custom-routing.service" | |
CUSTOM_ROUTING_SERVICE_FILE="/etc/systemd/system/${CUSTOM_ROUTING_SERVICE_NAME}" | |
SCRIPT_PATH="/usr/local/sbin/apply-custom-routing.sh" # Changed path to sbin | |
LOG_FILE="/var/log/network-config-script.log" | |
TEST_PING_COUNT=3 | |
RETRY_COUNT=3 | |
RETRY_DELAY=5 | |
DEFAULT_ROUTING_INTERFACE="ens18" # Interface for default route (DHCP) | |
SECONDARY_ROUTING_INTERFACE="ens19" # Interface for policy-based route (Static) | |
DEFAULT_TEST_TARGET="8.8.8.8" # Default target for general connectivity test | |
# Variables set by arguments | |
ENS19_STATIC_IP_CIDR="" # e.g., 192.168.2.100/24 | |
ENS19_GATEWAY="" # e.g., 192.168.2.1 | |
ENS19_IP_ADDRESS="" # Extracted IP from ENS19_STATIC_IP_CIDR | |
TEST_TARGET="${DEFAULT_TEST_TARGET}" # Target for testing | |
# --- Helper Functions --- | |
# Function to log messages with severity and timestamp | |
# Usage: msg <type> <message> | |
# Types: info, warning, error, success | |
msg() { | |
local type="$1" | |
local text="$2" | |
local color="" | |
local timestamp | |
timestamp=$(date "+%Y-%m-%d %H:%M:%S") | |
case "$type" in | |
"info") color="\e[34m" ;; | |
"warning") color="\e[33m" ;; | |
"error") color="\e[31m" ;; | |
"success") color="\e[32m" ;; | |
*) color="\e[0m" ;; # Default | |
esac | |
# Log to stderr and append to log file | |
echo -e "${color}${timestamp} [${type^^}] ${text}\e[0m" | tee -a "$LOG_FILE" >&2 | |
} | |
# Function to display usage instructions | |
usage() { | |
echo "Usage: $0 -s <ens19_static_ip/cidr> -g <ens19_gateway> [-t <test_target>] [-h]" | |
echo " -s : Static IP address and CIDR prefix for ${SECONDARY_ROUTING_INTERFACE} (e.g., 192.168.2.100/24)" | |
echo " -g : Gateway IP address for ${SECONDARY_ROUTING_INTERFACE}" | |
echo " -t : Optional IP address to ping for general connectivity testing (default: ${DEFAULT_TEST_TARGET})" | |
echo " -h : Display this help message" | |
exit 1 | |
} | |
# Check if running as root | |
check_root() { | |
if [[ "$EUID" -ne 0 ]]; then | |
msg "error" "This script must be run as root." | |
exit 1 | |
fi | |
# Ensure log file is writable (create if not exists) | |
touch "$LOG_FILE" || { msg "error" "Cannot write to log file $LOG_FILE"; exit 1; } | |
chown root:root "$LOG_FILE" | |
chmod 600 "$LOG_FILE" | |
msg "info" "Log file initialized: $LOG_FILE" | |
} | |
# Check if a command exists | |
# Usage: check_command <command_name> | |
check_command() { | |
local cmd="$1" | |
if ! command -v "$cmd" >/dev/null 2>&1; then | |
msg "error" "Command '$cmd' not found. Please install the package providing it." | |
# Suggest packages for common commands | |
case "$cmd" in | |
"netplan") echo "Try: sudo apt update && sudo apt install netplan.io" >&2 ;; | |
"ip") echo "Try: sudo apt update && sudo apt install iproute2" >&2 ;; | |
"ping") echo "Try: sudo apt update && sudo apt install iputils-ping" >&2 ;; | |
esac | |
exit 1 | |
fi | |
} | |
# Execute a command with retries | |
# Usage: execute_with_retry "<command>" "<description>" <retry_count> <retry_delay> | |
execute_with_retry() { | |
local cmd="$1" | |
local description="$2" | |
local retry_count="$3" | |
local retry_delay="$4" | |
local attempt=1 | |
local output | |
local result=1 | |
while [ $attempt -le "$retry_count" ]; do | |
msg "info" "Attempt $attempt/$retry_count: $description: Running '$cmd'" | |
# Capture stdout and stderr separately for better logging | |
output=$(eval "$cmd" 2>&1) # Use eval to handle complex commands/pipelines if needed, be cautious | |
result=$? | |
if [ $result -eq 0 ]; then | |
msg "success" "$description succeeded." | |
# Log successful output only if verbose logging is desired (optional) | |
# echo "Output:" >> "$LOG_FILE" | |
# echo "$output" >> "$LOG_FILE" | |
return 0 | |
else | |
msg "warning" "$description failed (Attempt $attempt/$retry_count). Exit code: $result" | |
echo "Output:" >> "$LOG_FILE" | |
echo "$output" >> "$LOG_FILE" # Log failure output | |
if [ $attempt -lt "$retry_count" ]; then | |
msg "warning" "Retrying in $retry_delay seconds..." | |
sleep "$retry_delay" | |
fi | |
fi | |
attempt=$((attempt + 1)) | |
done | |
msg "error" "$description failed after $retry_count attempts." | |
return 1 | |
} | |
# Function to roll back changes | |
rollback() { | |
msg "warning" "--- Initiating Rollback ---" | |
local rollback_failed=0 | |
# 1. Stop and disable the custom service | |
if systemctl is-active --quiet "$CUSTOM_ROUTING_SERVICE_NAME"; then | |
msg "info" "Stopping custom routing service..." | |
execute_with_retry "systemctl stop ${CUSTOM_ROUTING_SERVICE_NAME}" "Stop routing service" 2 2 || rollback_failed=1 | |
fi | |
if systemctl is-enabled --quiet "$CUSTOM_ROUTING_SERVICE_NAME"; then | |
msg "info" "Disabling custom routing service..." | |
execute_with_retry "systemctl disable ${CUSTOM_ROUTING_SERVICE_NAME}" "Disable routing service" 2 2 || rollback_failed=1 | |
fi | |
# 2. Remove the systemd service file | |
if [ -f "$CUSTOM_ROUTING_SERVICE_FILE" ]; then | |
msg "info" "Removing systemd service file..." | |
rm -f "$CUSTOM_ROUTING_SERVICE_FILE" || { msg "warning" "Failed to remove service file $CUSTOM_ROUTING_SERVICE_FILE"; rollback_failed=1; } | |
execute_with_retry "systemctl daemon-reload" "Reload systemd daemon" 2 2 # Important after removing unit file | |
fi | |
# 3. Remove the custom routing script | |
if [ -f "$SCRIPT_PATH" ]; then | |
msg "info" "Removing custom routing script..." | |
rm -f "$SCRIPT_PATH" || { msg "warning" "Failed to remove routing script $SCRIPT_PATH"; rollback_failed=1; } | |
fi | |
# 4. Restore Netplan configuration from backup | |
if [ -f "${NETPLAN_FILE}.bak" ]; then | |
msg "info" "Restoring Netplan configuration from ${NETPLAN_FILE}.bak..." | |
if cp -f "${NETPLAN_FILE}.bak" "${NETPLAN_FILE}"; then | |
msg "success" "Netplan config restored." | |
# 5. Apply the restored Netplan configuration | |
msg "info" "Applying restored Netplan configuration..." | |
if ! execute_with_retry "netplan apply" "Apply restored Netplan config" $RETRY_COUNT $RETRY_DELAY; then | |
msg "error" "Failed to apply restored Netplan config. Network state may be inconsistent." | |
rollback_failed=1 | |
else | |
msg "success" "Restored Netplan config applied." | |
fi | |
else | |
msg "error" "Failed to restore Netplan config from backup." | |
rollback_failed=1 | |
fi | |
else | |
msg "warning" "No Netplan backup file found (${NETPLAN_FILE}.bak). Cannot restore Netplan config." | |
# Attempt to apply current netplan config just in case it helps | |
msg "info" "Attempting to re-apply current Netplan configuration..." | |
execute_with_retry "netplan apply" "Re-apply current Netplan config" 1 0 | |
fi | |
if [[ $rollback_failed -ne 0 ]]; then | |
msg "error" "--- Rollback completed with errors. Manual intervention may be required. ---" | |
exit 1 # Exit with error code after rollback attempt | |
else | |
msg "success" "--- Rollback completed successfully. ---" | |
msg "info" "System state returned to before script execution (best effort)." | |
msg "info" "You might need to reboot or restart networking ('sudo systemctl restart systemd-networkd') for full effect." | |
fi | |
# Important: Don't exit the main script from here if called by trap | |
# Let the trap handler exit. If called directly, the caller should exit. | |
} | |
# Function to test network connectivity | |
test_connectivity() { | |
msg "info" "--- Testing Network Connectivity ---" | |
local test_failed=0 | |
# Test 1: General internet connectivity via default route (expected: ens18) | |
msg "info" "Testing general connectivity to ${TEST_TARGET} (expected via ${DEFAULT_ROUTING_INTERFACE})..." | |
if execute_with_retry "ping -c ${TEST_PING_COUNT} -W 2 ${TEST_TARGET}" "Ping test to ${TEST_TARGET}" 2 1; then | |
msg "success" "General connectivity test to ${TEST_TARGET} PASSED." | |
else | |
msg "error" "General connectivity test to ${TEST_TARGET} FAILED." | |
msg "warning" "Check if ${DEFAULT_ROUTING_INTERFACE} has DHCP lease and default route ('ip route show default')." | |
test_failed=1 | |
fi | |
# Test 2: Connectivity via the secondary interface's specific route | |
# We ping the gateway associated with ens19 using ens19's IP as the source. | |
if [ -z "$ENS19_IP_ADDRESS" ]; then | |
msg "error" "Cannot run secondary interface test: ENS19 IP address is not set." | |
test_failed=1 | |
elif [ -z "$ENS19_GATEWAY" ]; then | |
msg "error" "Cannot run secondary interface test: ENS19 Gateway is not set." | |
test_failed=1 | |
else | |
msg "info" "Testing connectivity to ${ENS19_GATEWAY} via ${SECONDARY_ROUTING_INTERFACE} (Source IP: ${ENS19_IP_ADDRESS})..." | |
# Ensure the interface is up before trying to ping from it | |
if ! ip link show dev "$SECONDARY_ROUTING_INTERFACE" up > /dev/null; then | |
msg "warning" "Interface ${SECONDARY_ROUTING_INTERFACE} is down. Attempting to bring it up..." | |
ip link set dev "$SECONDARY_ROUTING_INTERFACE" up | |
sleep 2 # Give it a moment | |
if ! ip link show dev "$SECONDARY_ROUTING_INTERFACE" up > /dev/null; then | |
msg "error" "Failed to bring up interface ${SECONDARY_ROUTING_INTERFACE}. Skipping test." | |
test_failed=1 | |
fi | |
fi | |
# Proceed with ping test if interface is up | |
if ip link show dev "$SECONDARY_ROUTING_INTERFACE" up > /dev/null; then | |
# Use -I flag to specify source IP/Interface | |
if execute_with_retry "ping -c ${TEST_PING_COUNT} -W 2 -I ${ENS19_IP_ADDRESS} ${ENS19_GATEWAY}" "Ping test from ${ENS19_IP_ADDRESS} to ${ENS19_GATEWAY}" 2 1; then | |
msg "success" "Secondary path connectivity test (Source ${ENS19_IP_ADDRESS} -> Gateway ${ENS19_GATEWAY}) PASSED." | |
else | |
msg "error" "Secondary path connectivity test (Source ${ENS19_IP_ADDRESS} -> Gateway ${ENS19_GATEWAY}) FAILED." | |
msg "warning" "Check policy routing ('ip rule show', 'ip route show table ${CUSTOM_ROUTE_TABLE_ID}') and firewall rules." | |
test_failed=1 | |
fi | |
fi | |
fi | |
if [[ $test_failed -ne 0 ]]; then | |
msg "error" "--- Network Connectivity Test FAILED ---" | |
return 1 | |
else | |
msg "success" "--- Network Connectivity Test PASSED ---" | |
return 0 | |
fi | |
} | |
# Check if specified network interfaces exist | |
check_interfaces() { | |
msg "info" "Checking if interfaces ${DEFAULT_ROUTING_INTERFACE} and ${SECONDARY_ROUTING_INTERFACE} exist..." | |
local iface | |
for iface in "$DEFAULT_ROUTING_INTERFACE" "$SECONDARY_ROUTING_INTERFACE"; do | |
if ! ip link show "$iface" > /dev/null 2>&1; then | |
msg "error" "Network interface '$iface' not found. Please verify interface names using 'ip link show'." | |
exit 1 | |
fi | |
done | |
msg "success" "Required interfaces found." | |
} | |
# --- Main Script --- | |
# Setup trap for unexpected errors - triggers rollback | |
trap 'msg "error" "An unexpected error occurred. Initiating rollback..."; rollback; exit 1' ERR | |
# 0. Initial Checks | |
check_root | |
check_command "netplan" | |
check_command "ip" | |
check_command "systemctl" | |
check_command "ping" # Make sure ping is available | |
# 1. Parse Arguments | |
msg "info" "Parsing command line arguments..." | |
while getopts "s:g:t:h" opt; do | |
case "$opt" in | |
s) ENS19_STATIC_IP_CIDR="$OPTARG" ;; | |
g) ENS19_GATEWAY="$OPTARG" ;; | |
t) TEST_TARGET="$OPTARG" ;; | |
h) usage ;; | |
\?) msg "error" "Invalid option: -$OPTARG" >&2; usage ;; | |
:) msg "error" "Option -$OPTARG requires an argument." >&2; usage ;; | |
esac | |
done | |
shift $((OPTIND -1)) | |
# Check for required arguments | |
if [ -z "$ENS19_STATIC_IP_CIDR" ] || [ -z "$ENS19_GATEWAY" ]; then | |
msg "error" "Missing required arguments: -s and -g are mandatory." | |
usage | |
fi | |
# Validate and extract IP from CIDR | |
if [[ ! "$ENS19_STATIC_IP_CIDR" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]]; then | |
msg "error" "Invalid format for static IP/CIDR (-s). Expected format: xxx.xxx.xxx.xxx/yy" | |
exit 1 | |
fi | |
ENS19_IP_ADDRESS=$(echo "$ENS19_STATIC_IP_CIDR" | cut -d'/' -f1) | |
# Validate Gateway IP format (basic) | |
if [[ ! "$ENS19_GATEWAY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
msg "error" "Invalid format for gateway IP (-g). Expected format: xxx.xxx.xxx.xxx" | |
exit 1 | |
fi | |
# Validate Interfaces | |
check_interfaces | |
msg "info" "--- Starting Network Configuration ---" | |
msg "info" "Configuration Parameters:" | |
msg "info" " Default Interface (DHCP): ${DEFAULT_ROUTING_INTERFACE}" | |
msg "info" " Secondary Interface (Static): ${SECONDARY_ROUTING_INTERFACE}" | |
msg "info" " Static IP/CIDR: ${ENS19_STATIC_IP_CIDR}" | |
msg "info" " Extracted IP: ${ENS19_IP_ADDRESS}" | |
msg "info" " Gateway: ${ENS19_GATEWAY}" | |
msg "info" " Connectivity Test Target: ${TEST_TARGET}" | |
msg "info" " Custom Routing Table ID: ${CUSTOM_ROUTE_TABLE_ID}" | |
msg "info" " Log File: ${LOG_FILE}" | |
# 2. Backup Existing Netplan Configuration | |
msg "info" "Backing up current Netplan configuration to ${NETPLAN_FILE}.bak..." | |
if ! cp -f "${NETPLAN_FILE}" "${NETPLAN_FILE}.bak"; then | |
msg "warning" "Failed to create Netplan backup. Proceeding without backup." | |
# Don't exit, allow proceeding but warn the user | |
fi | |
# 3. Create New Netplan Configuration | |
msg "info" "Generating new Netplan configuration file: ${NETPLAN_FILE}" | |
cat > "$NETPLAN_FILE" <<EOF | |
# Configuration generated by script $(basename "$0") on $(date) | |
network: | |
version: 2 | |
renderer: networkd | |
ethernets: | |
${DEFAULT_ROUTING_INTERFACE}: | |
dhcp4: true | |
dhcp4-overrides: | |
use-routes: true # Ensure DHCP supplies a default route | |
${SECONDARY_ROUTING_INTERFACE}: | |
dhcp4: false | |
addresses: | |
- ${ENS19_STATIC_IP_CIDR} | |
# NO gateway4 here - handled by policy routing script | |
# Tell netplan not to add a default route for this interface | |
routes: | |
- to: 0.0.0.0/0 | |
via: 0.0.0.0 # Invalid gateway forces no default route creation | |
metric: 200 # Higher metric just in case, but scope link is key | |
scope: link | |
# Optional: Add specific routes needed for this interface if any | |
# routes: | |
# - to: 10.10.10.0/24 | |
# via: ${ENS19_GATEWAY} | |
EOF | |
if [ $? -ne 0 ]; then | |
msg "error" "Failed to write Netplan configuration to ${NETPLAN_FILE}." | |
rollback # Attempt rollback | |
exit 1 | |
fi | |
msg "success" "Netplan configuration file created." | |
# 4. Create the Custom Routing Script | |
msg "info" "Creating custom routing script: ${SCRIPT_PATH}" | |
cat > "$SCRIPT_PATH" <<EOF | |
#!/bin/bash | |
# This script applies policy-based routing rules for ${SECONDARY_ROUTING_INTERFACE}. | |
# Generated by $(basename "$0") on $(date) | |
# Use logger for messages within this script | |
log_msg() { | |
logger -t custom-routing "\$1" | |
} | |
log_msg "Applying custom routing rules..." | |
# Ensure the custom table exists, flush if it does | |
ip route flush table ${CUSTOM_ROUTE_TABLE_ID} || log_msg "Table ${CUSTOM_ROUTE_TABLE_ID} might not exist yet, ignoring flush error." | |
# Add the default route for the secondary interface to the custom table | |
# Use 'replace' to ensure it's updated if the script runs again | |
ip route replace default via ${ENS19_GATEWAY} dev ${SECONDARY_ROUTING_INTERFACE} table ${CUSTOM_ROUTE_TABLE_ID} | |
if [ \$? -ne 0 ]; then | |
log_msg "ERROR: Failed to add route to table ${CUSTOM_ROUTE_TABLE_ID} via ${ENS19_GATEWAY} dev ${SECONDARY_ROUTING_INTERFACE}" | |
exit 1 | |
fi | |
log_msg "Added default route to table ${CUSTOM_ROUTE_TABLE_ID} via ${ENS19_GATEWAY} dev ${SECONDARY_ROUTING_INTERFACE}" | |
# Add the rule to use the custom table for traffic *from* the secondary interface's IP | |
# Use a priority lower than the default 'main' table rule (usually 32766) | |
RULE_PRIORITY=10000 | |
# Delete existing rule first (if any) to avoid duplicates | |
ip rule del from ${ENS19_IP_ADDRESS}/32 lookup ${CUSTOM_ROUTE_TABLE_ID} prio ${RULE_PRIORITY} 2>/dev/null | |
ip rule add from ${ENS19_IP_ADDRESS}/32 lookup ${CUSTOM_ROUTE_TABLE_ID} prio ${RULE_PRIORITY} | |
if [ \$? -ne 0 ]; then | |
log_msg "ERROR: Failed to add rule 'from ${ENS19_IP_ADDRESS}/32 table ${CUSTOM_ROUTE_TABLE_ID} prio ${RULE_PRIORITY}'" | |
# Attempt cleanup of route table entry on rule failure | |
ip route flush table ${CUSTOM_ROUTE_TABLE_ID} | |
exit 1 | |
fi | |
log_msg "Added rule: from ${ENS19_IP_ADDRESS}/32 lookup ${CUSTOM_ROUTE_TABLE_ID} prio ${RULE_PRIORITY}" | |
# Ensure the main default route (via ens18/DHCP) is present. Netplan should handle this. | |
# We log a warning if it's missing, but don't try to fix it here. | |
if ! ip route show default | grep -q "dev ${DEFAULT_ROUTING_INTERFACE}"; then | |
log_msg "WARNING: Default route via ${DEFAULT_ROUTING_INTERFACE} not found. Check DHCP configuration for ${DEFAULT_ROUTING_INTERFACE}." | |
fi | |
log_msg "Custom routing rules applied successfully." | |
exit 0 | |
EOF | |
if [ $? -ne 0 ]; then | |
msg "error" "Failed to write custom routing script to ${SCRIPT_PATH}." | |
rollback # Attempt rollback | |
exit 1 | |
fi | |
chmod +x "$SCRIPT_PATH" | |
if [ $? -ne 0 ]; then | |
msg "error" "Failed to make routing script ${SCRIPT_PATH} executable." | |
rollback # Attempt rollback | |
exit 1 | |
fi | |
msg "success" "Custom routing script created and made executable." | |
# 5. Create the systemd Service File | |
msg "info" "Creating systemd service file: ${CUSTOM_ROUTING_SERVICE_FILE}" | |
cat > "$CUSTOM_ROUTING_SERVICE_FILE" <<EOF | |
# Service file generated by $(basename "$0") on $(date) | |
[Unit] | |
Description=Apply Custom Policy-Based Routing for ${SECONDARY_ROUTING_INTERFACE} | |
After=network-online.target | |
Wants=network-online.target | |
# If problems arise with network-online.target, consider: | |
# After=systemd-networkd.service NetworkManager.service | |
# Wants=systemd-networkd.service NetworkManager.service | |
[Service] | |
Type=oneshot | |
ExecStart=${SCRIPT_PATH} | |
RemainAfterExit=yes | |
[Install] | |
WantedBy=multi-user.target | |
EOF | |
if [ $? -ne 0 ]; then | |
msg "error" "Failed to create systemd service file ${CUSTOM_ROUTING_SERVICE_FILE}." | |
rollback # Attempt rollback | |
exit 1 | |
fi | |
msg "success" "Systemd service file created." | |
# 6. Reload systemd, Enable and Start the Service | |
msg "info" "Reloading systemd daemon, enabling and starting custom routing service..." | |
execute_with_retry "systemctl daemon-reload" "Reload systemd daemon" $RETRY_COUNT $RETRY_DELAY || { rollback; exit 1; } | |
execute_with_retry "systemctl enable ${CUSTOM_ROUTING_SERVICE_NAME}" "Enable routing service" $RETRY_COUNT $RETRY_DELAY || { rollback; exit 1; } | |
# Don't start yet, apply Netplan first to ensure interfaces are configured | |
# 7. Apply Netplan Configuration | |
msg "info" "Applying Netplan configuration..." | |
# Use netplan generate first to catch syntax errors early | |
msg "info" "Running 'netplan generate' to check syntax..." | |
if ! sudo netplan generate; then | |
msg "error" "Netplan configuration syntax error. Check ${NETPLAN_FILE}." | |
# Attempt to show the error details from journalctl | |
msg "info" "Checking journalctl for netplan errors (may need scrolling up):" | |
journalctl -n 50 | grep -i netplan || true | |
rollback | |
exit 1 | |
fi | |
msg "success" "Netplan configuration syntax check passed." | |
msg "info" "Running 'netplan apply'..." | |
if ! execute_with_retry "netplan apply" "Apply Netplan configuration" $RETRY_COUNT $RETRY_DELAY; then | |
msg "error" "Failed to apply Netplan configuration. Check logs (journalctl -u systemd-networkd)." | |
rollback | |
exit 1 | |
fi | |
msg "success" "Netplan configuration applied." | |
msg "info" "Waiting a few seconds for interfaces to potentially settle..." | |
sleep 5 | |
# 8. Start the Custom Routing Service (after Netplan apply) | |
msg "info" "Starting custom routing service..." | |
if ! execute_with_retry "systemctl restart ${CUSTOM_ROUTING_SERVICE_NAME}" "Start/Restart routing service" $RETRY_COUNT $RETRY_DELAY; then | |
msg "error" "Failed to start custom routing service ${CUSTOM_ROUTING_SERVICE_NAME}." | |
msg "info" "Check service status: systemctl status ${CUSTOM_ROUTING_SERVICE_NAME}" | |
msg "info" "Check script logs: journalctl -u ${CUSTOM_ROUTING_SERVICE_NAME} or logger output in /var/log/syslog" | |
rollback | |
exit 1 | |
fi | |
msg "success" "Custom routing service started." | |
# 9. Final Validation | |
msg "info" "Running final network connectivity tests..." | |
if ! test_connectivity; then | |
msg "error" "Network configuration validation failed after applying changes." | |
msg "error" "Initiating rollback due to failed validation." | |
rollback | |
exit 1 | |
fi | |
# 10. Success! | |
msg "success" "--- Network Configuration Successfully Applied and Validated! ---" | |
msg "info" "Summary of changes:" | |
msg "info" " - Netplan configuration updated: ${NETPLAN_FILE}" | |
msg "info" " - Custom routing script installed: ${SCRIPT_PATH}" | |
msg "info" " - Systemd service enabled and started: ${CUSTOM_ROUTING_SERVICE_NAME}" | |
msg "info" "Policy routing rules should now be active." | |
msg "info" "Verify with: 'ip rule show' and 'ip route show table ${CUSTOM_ROUTE_TABLE_ID}'" | |
msg "info" "Configuration is designed to persist across reboots." | |
msg "info" "To revert changes manually, run 'sudo ${0} --rollback' (hypothetical future feature) or manually reverse steps/restore backup." | |
# Disable trap before exiting normally | |
trap - ERR | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment