root/lib/cyborghood/services/dns.rb @ 766c702c
d32ee48a | Marc Dequenes | #--
|
|
# CyborgHood, a distributed system management software.
|
|||
e7315259 | Marc Dequènes (Duck) | # Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
|
|
d32ee48a | Marc Dequenes | #
|
|
# This program is free software: you can redistribute it and/or modify
|
|||
# it under the terms of the GNU General Public License as published by
|
|||
# the Free Software Foundation, either version 3 of the License, or
|
|||
# (at your option) any later version.
|
|||
#
|
|||
# This program is distributed in the hope that it will be useful,
|
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
# GNU General Public License for more details.
|
|||
#
|
|||
# You should have received a copy of the GNU General Public License
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#++
|
|||
157c68c9 | Marc Dequenes | require 'fileutils'
|
|
4153cce4 | Marc Dequenes | module CyborgHood
|
|
module Services
|
|||
class DNS
|
|||
157c68c9 | Marc Dequenes | attr_reader :zone
|
|
attr_accessor :content
|
|||
4153cce4 | Marc Dequenes | def initialize(zone)
|
|
@zone = zone
|
|||
@config = Config.instance
|
|||
157c68c9 | Marc Dequenes | @content = nil
|
|
fba691ae | Marc Dequenes | @filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
|
|
# TODO: should be checked at startup time
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if @filename == @config.dns.master_zone_pattern
|
|||
157c68c9 | Marc Dequenes | # ensure we can find the needed programs (should be handled somewhere else)
|
|
ENV['PATH'] = (ENV['PATH'].split(":") + ["/sbin", "/usr/sbin", "/usr/local/sbin"]).uniq.join(":")
|
|||
4153cce4 | Marc Dequenes | end
|
|
def read_zone
|
|||
begin
|
|||
fba691ae | Marc Dequenes | @content = File.read(@filename)
|
|
4153cce4 | Marc Dequenes | rescue
|
|
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' cannot be read (nonexistent or lack of permission)")
|
|||
end
|
|||
end
|
|||
157c68c9 | Marc Dequenes | ||
def serial
|
|||
766c702c | Marc Dequènes (Duck) | # TODO: use a factory
|
|
157c68c9 | Marc Dequenes | case @config.dns.nameserver || :bind
|
|
when :bind
|
|||
output = []
|
|||
begin
|
|||
fba691ae | Marc Dequenes | IO.popen("named-checkzone -i none '#{@zone}' #{@filename}") do |fp|
|
|
157c68c9 | Marc Dequenes | output << fp.gets.chomp! until fp.eof?
|
|
end
|
|||
rescue
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "zone serial for '#{@zone}' could not be found")
|
|||
end
|
|||
if $? == 0
|
|||
serial = nil
|
|||
output.each do |l|
|
|||
if l =~ /: loaded serial (\d+)$/
|
|||
serial = $1
|
|||
break
|
|||
end
|
|||
end
|
|||
if serial
|
|||
serial
|
|||
else
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "no serial returned")
|
|||
end
|
|||
else
|
|||
nil
|
|||
end
|
|||
else
|
|||
# TODO: should be checked at startup time
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: unknown nameserver")
|
|||
end
|
|||
end
|
|||
def check_zone_file(filename)
|
|||
766c702c | Marc Dequènes (Duck) | # TODO: use a factory
|
|
157c68c9 | Marc Dequenes | case @config.dns.nameserver || :bind
|
|
when :bind
|
|||
output = []
|
|||
begin
|
|||
IO.popen("named-checkzone '#{@zone}' #{filename}") do |fp|
|
|||
output << fp.gets.chomp! until fp.eof?
|
|||
end
|
|||
rescue
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' could not be checked")
|
|||
end
|
|||
if $? == 0
|
|||
serial = nil
|
|||
messages = []
|
|||
output.each do |l|
|
|||
next if l == "OK"
|
|||
if l =~ /: loaded serial (\d+)$/
|
|||
serial = $1
|
|||
next
|
|||
end
|
|||
messages << l
|
|||
end
|
|||
if serial
|
|||
return {:ok => true, :serial => serial, :warnings => messages}.to_ostruct
|
|||
else
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "zone validated but no serial returned")
|
|||
end
|
|||
else
|
|||
return {:ok => false, :errors => messages}.to_ostruct
|
|||
end
|
|||
else
|
|||
# TODO: should be checked at startup time
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: unknown nameserver")
|
|||
end
|
|||
end
|
|||
def write_zone_from_file(new_zone_filename)
|
|||
fba691ae | Marc Dequenes | # create backup
|
|
FileUtils.cp(@filename, backup_zone_filename())
|
|||
FileUtils.cp(new_zone_filename, @filename)
|
|||
end
|
|||
def replace_zone_with_backup
|
|||
if File.exists?(backup_zone_filename())
|
|||
FileUtils.cp(backup_zone_filename(), @filename)
|
|||
else
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "no zone backup file found")
|
|||
end
|
|||
end
|
|||
def reload_zone
|
|||
766c702c | Marc Dequènes (Duck) | script = @config.dns.update_zone_script
|
|
if script.nil?
|
|||
case @config.dns.nameserver || :bind
|
|||
when :bind
|
|||
script = "rndc reload"
|
|||
else
|
|||
# TODO: should be checked at startup time
|
|||
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: unknown nameserver")
|
|||
end
|
|||
end
|
|||
system "sudo #{script} '#{@zone}' >/dev/null"
|
|||
fba691ae | Marc Dequenes | end
|
|
private
|
|||
157c68c9 | Marc Dequenes | ||
fba691ae | Marc Dequenes | def backup_zone_filename
|
|
@filename + ".ch-backup"
|
|||
157c68c9 | Marc Dequenes | end
|
|
4153cce4 | Marc Dequenes | end
|
|
end
|
|||
end
|