drsound/fault_tolerant_router

View on GitHub
bin/fault_tolerant_router

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby

require 'optparse'
require 'net/smtp'
require 'mail'
require 'logger'
require 'socket'
require 'yaml'
require 'fault_tolerant_router/version'
require 'fault_tolerant_router/generate_config'
require 'fault_tolerant_router/generate_iptables'
require 'fault_tolerant_router/monitor'
require 'fault_tolerant_router/uplink'
require 'fault_tolerant_router/uplinks'

CONFIG_TEMPLATE = {
    'uplinks' => {
        'interface' => {},
        'type' => {},
        'ip' => {},
        'gateway' => {},
        'description' => {},
        'weight' => {},
        'priority_group' => {}
    },
    'downlinks' => {
        'lan' => {},
        'dmz' => {}
    },
    'tests' => {
        'ips' => {},
        'required_successful' => {},
        'ping_retries' => {},
        'interval' => {}
    },
    'log' => {
        'file' => {},
        'max_size' => {},
        'old_files' => {}
    },
    'email' => {
        'send' => {},
        'sender' => {},
        'recipients' => {},
        'smtp_parameters' => {
            'address' => {},
            'port' => {},
            'authentication' => {},
            'enable_starttls_auto' => {},
            'user_name' => {},
            'password' => {}
        }
    },
    'base_table' => {},
    'base_priority' => {},
    'base_fwmark' => {}
}

def check_config(config, template, path = [])
  config.each do |k, v|
    unless template.keys.include?(k)
      puts "Error: unknown configuration parameter '#{(path + [k]).join('/')}'"
      exit 1
    end
    case v
      when Hash
        check_config(v, template[k], path + [k])
      when Array
        v.each { |v2| check_config(v2, template[k], path + [k]) if v2.is_a?(Hash) }
    end
  end
end

options = {
    configuration_file: '/etc/fault_tolerant_router.conf',
    debug: false,
    demo: false
}
parser = OptionParser.new do |opts|
  opts.banner = "Usage: #{File.basename($0)} [OPTION]... ACTION"
  opts.separator ''
  opts.separator 'ACTION = generate_config|generate_iptables|email_test|monitor'
  opts.separator ''
  opts.separator '         generate_config: save an example configuration'
  opts.separator '         generate_iptables: generate an "iptables-save" configuration'
  opts.separator '         email_test: send a test email to verify the correctness of SMTP parameters'
  opts.separator '         monitor: monitor uplinks and change routing accordingly'
  opts.separator ''
  opts.separator 'OPTIONS:'
  opts.on('--config=FILE', 'Configuration file (default /etc/fault_tolerant_router.conf)') do |configuration_file|
    options[:configuration_file] = configuration_file
  end
  opts.separator '        "monitor" specific options:'
  opts.on('--debug', 'Print debug output') do |debug|
    options[:debug] = debug
  end
  opts.on('--demo', 'Demo the program by faking random uplink failures') do |demo|
    options[:demo] = demo
  end
  opts.separator ''
  opts.separator "Version #{FaultTolerantRouter::VERSION}"
  opts.separator 'Alessandro Zarrilli <alessandro@zarrilli.net>'
  opts.separator 'https://github.com/drsound/fault_tolerant_router'
end
begin
  parser.parse!
rescue OptionParser::ParseError
  puts parser.help
  exit 1
end

DEMO = options[:demo]
#activate debug if in demo mode
DEBUG = options[:debug] || DEMO

unless ARGV.size == 1 && %w(generate_config generate_iptables email_test monitor).include?(ARGV[0])
  puts parser.help
  exit 1
end

if ARGV[0] == 'generate_config'
  generate_config(options[:configuration_file])
  exit 0
end

unless File.exists?(options[:configuration_file])
  puts "Configuration file #{options[:configuration_file]} does not exists!"
  exit 1
end

config = YAML.load_file(options[:configuration_file])
check_config(config, CONFIG_TEMPLATE)

LAN_INTERFACE = config['downlinks']['lan']
DMZ_INTERFACE = config['downlinks']['dmz']
TEST_IPS = config['tests']['ips']
REQUIRED_SUCCESSFUL_TESTS = config['tests']['required_successful']
PING_RETRIES = config['tests']['ping_retries']
TEST_INTERVAL = config['tests']['interval']
LOG_FILE = config['log']['file']
LOG_MAX_SIZE = config['log']['max_size']
LOG_OLD_FILES = config['log']['old_files']
SEND_EMAIL = config['email']['send']
EMAIL_SENDER = config['email']['sender']
EMAIL_RECIPIENTS = config['email']['recipients']
SMTP_PARAMETERS = Hash[config['email']['smtp_parameters'].map { |k, v| [k.to_sym, v] }]
BASE_TABLE = config['base_table']
BASE_PRIORITY = config['base_priority']
BASE_FWMARK = config['base_fwmark']
UPLINKS = Uplinks.new(config['uplinks'])

case ARGV[0]
  when 'generate_iptables'
    generate_iptables
  when 'email_test'
    begin
      send_email('fault_tolerant_router test')
      puts "Test email sent to #{EMAIL_RECIPIENTS.join(', ')}"
    rescue Exception => e
      puts "Error sending email: #{e}"
    end
  else
    monitor
end