class Proxy::DHCP::Dnsmasq::SubnetService

Attributes

config_dir[R]
lease_file[R]

Public Class Methods

new(config, target_dir, lease_file, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name) click to toggle source
Calls superclass method
# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 12
def initialize(config, target_dir, lease_file, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
  @config_paths = [config].flatten
  @target_dir = target_dir
  @lease_file = lease_file

  super(leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
end

Public Instance Methods

add_watch() click to toggle source
# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 28
def add_watch
  # TODO: Add proper inotify listener for configs
  @inotify = INotify::Notifier.new
  @inotify.watch(File.dirname(lease_file), :modify, :moved_to) do |ev|
    next unless ev.absolute_name == lease_file

    leases = load_leases

    # FIXME: Proper method for this
    m.synchronize do
      leases_by_ip.clear
      leases_by_mac.clear
    end
    leases.each { |l| add_lease(l.subnet_address, l) }
  end
end
load!() click to toggle source
# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 20
def load!
  add_subnet(parse_config_for_subnet)
  load_subnet_data
  #add_watch # TODO

  true
end
load_leases() click to toggle source

Expects subnet_service to have subnet data

# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 241
def load_leases
  open(@lease_file, 'r').readlines.map do |line|
    timestamp, mac, ip, _hostname, _client_id = line.split
    timestamp = timestamp.to_i

    subnet = find_subnet(ip)
    ::Proxy::DHCP::Lease.new(
      nil, ip, mac, subnet,
      timestamp - (@ttl || 24 * 60 * 60),
      timestamp,
      'active'
    )
  end
end
load_subnet_data() click to toggle source
# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 212
def load_subnet_data
  reservations = parse_config_for_dhcp_reservations
  reservations.each do |record|
    if dupe = find_host_by_mac(record.subnet_address, record.mac)
      logger.debug "Found duplicate #{dupe} when adding record #{record}, skipping"
      next
    end

    # logger.debug "Adding host #{record}"
    add_host(record.subnet_address, record)
  end

  leases = load_leases
  leases.each do |lease|
    if dupe = find_lease_by_mac(lease.subnet_address, lease.mac)
      logger.debug "Found duplicate #{dupe} by MAC when adding lease #{lease}, skipping"
      next
    end
    if dupe = find_lease_by_ip(lease.subnet_address, lease.ip)
      logger.debug "Found duplicate #{dupe} by IP when adding lease #{lease}, skipping"
      next
    end

    # logger.debug "Adding lease #{lease}"
    add_lease(lease.subnet_address, lease)
  end
end
parse_config_for_dhcp_reservations() click to toggle source

Expects subnet_service to have subnet data

# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 110
def parse_config_for_dhcp_reservations
  to_ret = {}
  files = []
  @config_paths.each do |path|
    files << path if File.exist? path
    files += Dir[File.join(path), '*'] if Dir.exist? path
  end

  logger.debug "Starting parse of DHCP reservations from #{files}"
  files.each do |file|
    logger.debug "  Parsing #{file}..."
    open(file, 'r').each_line do |line|
      line.strip!
      next if line.empty? || line.start_with?('#') || !line.include?('=')

      option, value = line.split('=')
      case option
      when 'dhcp-host'
        data = value.split(',')
        data.shift while data.first.start_with? 'set:'

        mac, ip, hostname = data[0, 3]

        # TODO: Possible ttl on end

        subnet = find_subnet(ip)
        to_ret[mac] = ::Proxy::DHCP::Reservation.new(
          hostname,
          ip,
          mac,
          subnet,
          # :source_file => file # TODO: Needs to overload the comparison
        )
      when 'dhcp-boot'
        data = value.split(',')
        if data.first.start_with? 'tag:'
          mac = data.first[4..-1]
          data.shift

          next unless to_ret.key? mac

          file, server = data

          to_ret[mac].options[:nextServer] = file
          to_ret[mac].options[:filename] = server
        end
      end
    end
  end
  dhcpoptions = {}

  dhcpopts_path = File.join(@target_dir, 'dhcpopts.conf')
  logger.debug "Parsing DHCP options from #{dhcpopts_path}"
  if File.exist? dhcpopts_path
    open(dhcpopts_path, 'r').each_line do |line|
      data = line.strip.split(',')
      next if data.empty? || !data.first.start_with?('tag:')

      tag = data.first[4..-1]
      data.shift

      dhcpoptions[tag] = data.last
    end
  end

  logger.debug "Parsing provisioned DHCP reservations from #{@target_dir}"
  Dir[File.join(@target_dir, 'dhcphosts', '*')].each do |file|
    logger.debug "  Parsing #{file}..."
    open(file, 'r').each_line do |line|
      data = line.strip.split(',')
      next if data.empty? || data.first.start_with?('#')

      mac = data.first
      data.shift

      options = { :deletable => true }
      while data.first.start_with? 'set:'
        tag = data.first[4..-1]
        data.shift

        value = dhcpoptions[tag]
        next if value.nil?

        options[:nextServer] = value if tag.start_with? 'ns'
        options[:filename] = value if tag.start_with? 'bf'
      end

      ip, name = data
      subnet = find_subnet(ip)

      to_ret[mac] = ::Proxy::DHCP::Reservation.new(
        name, ip, mac, subnet, options
      )
    end
  end

  to_ret.values
rescue StandardError => e
  logger.error msg = "Unable to parse reservations: #{e}"
  raise Proxy::DHCP::Error, e, msg
end
parse_config_for_subnet() click to toggle source
# File lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb, line 45
def parse_config_for_subnet
  configuration = { options: {} }
  files = []
  @config_paths.each do |path|
    files << path if File.exist? path
    files += Dir["#{path}/*"] if Dir.exist? path
  end

  logger.debug "Starting parse of DHCP subnets from #{files}"
  files.each do |file|
    logger.debug "  Parsing #{file}..."
    open(file, 'r').each_line do |line|
      line.strip!
      next if line.empty? || line.start_with?('#') || !line.include?('=')

      option, value = line.split('=')
      case option
      when 'dhcp-leasefile'
        next if @lease_file

        @lease_file = value
      when 'dhcp-range'
        data = value.split(',')

        ttl = data.pop
        mask = data.pop
        range_to = data.pop
        range_from = data.pop

        ttl = case ttl[-1]
              when 'h'
                ttl[0..-2].to_i * 60 * 60
              when 'm'
                ttl[0..-2].to_i * 60
              else
                ttl.to_i
              end

        configuration.merge!                address: IPAddr.new("#{range_from}/#{mask}").to_s,
          mask: mask,
          range: [range_from, range_to],
          ttl: ttl
      when 'dhcp-option'
        data = value.split(',')

        data.shift until data.empty? || /\A\d+\z/ === data.first
        next if data.empty?

        code = data.shift.to_i
        option = ::Proxy::DHCP::Standard.select { |_k, v| v[:code] == code }.first.first

        data = data.first unless ::Proxy::DHCP::Standard[option][:is_list]
        configuration[:options][option] = data
      end
    end
  end

  # TODO: Multiple subnets
  logger.debug "Adding subnet with configuration; #{configuration}"
  @ttl = configuration[:ttl]
  ::Proxy::DHCP::Subnet.new(configuration[:address], configuration[:mask], configuration[:options])
end