Revision ca79e27e
Added by Marc Dequènes over 14 years ago
- ID ca79e27e4f538a8ddfbfccb3342ea3c1fe0167c6
bin/mapmaker | ||
---|---|---|
include CyborgServerInterface
|
||
include CyborgServerDefaultInterface
|
||
|
||
class DNS < Services::DNS
|
||
class DNS < Services::DNS::System
|
||
include CyborgServerInterface
|
||
|
||
export_parent_methods
|
||
... | ... | |
include CyborgServerInterface
|
||
|
||
def initialize
|
||
@dns = Services::DNS.new
|
||
@dns = Services::DNS::System.new
|
||
end
|
||
|
||
def api_methods
|
||
... | ... | |
end
|
||
end
|
||
|
||
class Zone < Services::Zone
|
||
class Zone < Services::DNS::Zone
|
||
include CyborgServerEmbededInterface
|
||
end
|
||
end
|
lib/cyborghood/cyborg/interface.rb | ||
---|---|---|
# preliminary outgoing message handling
|
||
action.call(*formated_data).to_yaml
|
||
rescue
|
||
logger.debug "node action error message: " + $!
|
||
logger.debug "node action error backtrace: " + $!.backtrace.join("\n")
|
||
return "550 method call failed: " + $!
|
||
end
|
||
end
|
lib/cyborghood/cyborg/server.rb | ||
---|---|---|
end
|
||
cmd = $1
|
||
flags = $2 || ""
|
||
pp "GROK"
|
||
pp cmd
|
||
pp flags
|
||
|
||
if flags.index '?'
|
||
send_line "250+ ok"
|
||
send_line({'exists?' => @interface.has_node?(cmd)}.to_yaml)
|
||
send_line({'exists' => @interface.has_node?(cmd)}.to_yaml)
|
||
return
|
||
end
|
||
|
lib/cyborghood/services/dns.rb | ||
---|---|---|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
#++
|
||
|
||
require 'digest/md5'
|
||
require 'tempfile'
|
||
require 'fileutils'
|
||
|
||
# ensure we can find the needed programs (should be handled somewhere else)
|
||
... | ... | |
|
||
module CyborgHood
|
||
module Services
|
||
class DNS
|
||
def initialize
|
||
@config = Config.instance
|
||
module DNS
|
||
class System
|
||
def initialize
|
||
@config = Config.instance
|
||
|
||
@zone_files_pattern = @config.dns.master_zone_pattern.gsub("#ZONE#", "*")
|
||
@zone_files_regex = Regexp.new("^" + @config.dns.master_zone_pattern.gsub("#ZONE#", "(.*)") + "$")
|
||
end
|
||
@zone_files_pattern = @config.dns.master_zone_pattern.gsub("#ZONE#", "*")
|
||
@zone_files_regex = Regexp.new("^" + @config.dns.master_zone_pattern.gsub("#ZONE#", "(.*)") + "$")
|
||
end
|
||
|
||
def zones(zone = nil)
|
||
if zone.nil?
|
||
def zones
|
||
Dir.glob(@zone_files_pattern).collect do |file|
|
||
$1 if file =~ @zone_files_regex
|
||
end
|
||
else
|
||
end
|
||
|
||
def [](zone)
|
||
return unless zones.include?(zone)
|
||
|
||
Zone.new(zone)
|
||
end
|
||
|
||
def check_zone_file(check_type)
|
||
case @config.dns.software
|
||
when 'bind'
|
||
output = []
|
||
begin
|
||
IO.popen("named-checkconf '#{@zone}' #{filename}") do |fp|
|
||
output << fp.gets.chomp! until fp.eof?
|
||
end
|
||
rescue
|
||
raise CyberError.new(:unrecoverable, "services/dns", "global configuration could not be checked (I/O error)")
|
||
end
|
||
|
||
if $?.success?
|
||
return {:ok => true, :warnings => output}.to_ostruct
|
||
else
|
||
return {:ok => false, :errors => output}.to_ostruct
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
class Zone
|
||
attr_reader :zone
|
||
attr_accessor :content
|
||
class Zone
|
||
def initialize(zone)
|
||
@zone = zone
|
||
|
||
def initialize(zone)
|
||
@zone = zone
|
||
@config = Config.instance
|
||
@content = nil
|
||
@temp_file = nil
|
||
|
||
@config = Config.instance
|
||
@content = nil
|
||
@filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
|
||
|
||
@filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
|
||
end
|
||
at_exit {cleanup_temp}
|
||
end
|
||
|
||
def read_zone
|
||
begin
|
||
@content = File.read(@filename)
|
||
rescue
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' cannot be read (nonexistent or lack of permission)")
|
||
def content
|
||
read_zone(@filename) if @content.nil?
|
||
@content
|
||
end
|
||
end
|
||
|
||
def serial
|
||
case @config.dns.software
|
||
when 'bind'
|
||
output = []
|
||
begin
|
||
IO.popen("named-checkzone -i none '#{@zone}' #{@filename}") do |fp|
|
||
output << fp.gets.chomp! until fp.eof?
|
||
end
|
||
rescue
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone serial for '#{@zone}' could not be found (I/O error)")
|
||
def content=(txt)
|
||
@content = txt
|
||
end
|
||
|
||
def changed?
|
||
return false if @content.nil?
|
||
|
||
# if original hash is missing, save zone content, reload
|
||
# original file, compute hash, and restore previous content
|
||
if @content_hash.nil?
|
||
content_backup = @content
|
||
@content = nil
|
||
content
|
||
@content = content_backup
|
||
end
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone serial for '#{@zone}' could not be found (#{output.first})") unless $?.success?
|
||
|
||
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
|
||
Digest::MD5.hexdigest(@content) != @content_hash
|
||
end
|
||
|
||
def cancel_changes
|
||
@content = nil
|
||
cleanup_temp
|
||
end
|
||
|
||
def check(force_real = false)
|
||
check_zone_file('full', force_real = false)
|
||
end
|
||
|
||
def serial(force_real = false)
|
||
r = check_zone_file('none', force_real = false)
|
||
if r.ok
|
||
r.serial
|
||
else
|
||
nil
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone serial for '#{@zone}' could not be found (#{r.errors.first})")
|
||
end
|
||
end
|
||
end
|
||
|
||
def check_zone_file(filename)
|
||
case @config.dns.software
|
||
when 'bind'
|
||
output = []
|
||
def import_from_file(new_zone_filename)
|
||
read_zone(new_zone_filename)
|
||
end
|
||
|
||
def import_from_backup
|
||
read_zone(self.backup_filename)
|
||
end
|
||
|
||
def create_backup
|
||
FileUtils.cp(@filename, backup_filename())
|
||
end
|
||
|
||
def save
|
||
raise CyberError.new(:unrecoverable, "services/dns", "won't save an empty zone file") if @content.nil?
|
||
write_zone(@filename)
|
||
update_hash
|
||
cleanup_temp
|
||
end
|
||
|
||
def activate
|
||
system "sudo rndc reload '#{@zone}' >/dev/null"
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone activation failed") unless $?.success?
|
||
end
|
||
|
||
protected
|
||
|
||
def backup_filename
|
||
@filename + ".ch-backup"
|
||
end
|
||
|
||
def save_to_temp
|
||
return unless temp_file.nil?
|
||
|
||
begin
|
||
IO.popen("named-checkzone '#{@zone}' #{filename}") do |fp|
|
||
output << fp.gets.chomp! until fp.eof?
|
||
end
|
||
@temp_file = Tempfile.new(@zone)
|
||
@temp_file.write(@content)
|
||
@temp_file.close
|
||
rescue
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' could not be checked")
|
||
raise CyberError.new(:unrecoverable, "services/dns", "could not save temporary zone")
|
||
end
|
||
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
|
||
def cleanup_temp
|
||
return if @temp_file.nil?
|
||
|
||
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
|
||
@temp_file.close!
|
||
@temp_file = nil
|
||
end
|
||
|
||
def temp_filename
|
||
@temp_file.path
|
||
end
|
||
|
||
def current_filename
|
||
if changed?
|
||
save_to_temp
|
||
return temp_filename
|
||
end
|
||
|
||
@filename
|
||
end
|
||
end
|
||
|
||
def write_zone_from_file(new_zone_filename)
|
||
# create backup
|
||
FileUtils.cp(@filename, backup_zone_filename())
|
||
def update_hash
|
||
@content_hash = Digest::MD5.hexdigest(@content)
|
||
end
|
||
|
||
FileUtils.cp(new_zone_filename, @filename)
|
||
end
|
||
def read_zone(filename)
|
||
begin
|
||
@content = File.read(filename)
|
||
update_hash if filename == @filename
|
||
rescue
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' cannot be read from '#{filename}' (I/O error, nonexistent or lack of permission)")
|
||
end
|
||
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")
|
||
def write_zone(filename)
|
||
begin
|
||
File.open(filename, "w") do |fp|
|
||
fp.print @content
|
||
end
|
||
rescue
|
||
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' cannot be written from '#{filename}' (I/O error or lack of permission)")
|
||
end
|
||
end
|
||
end
|
||
|
||
def reload_zone
|
||
system "sudo rndc reload '#{@zone}' >/dev/null"
|
||
end
|
||
def check_zone_file(check_type, force_real = false)
|
||
filename = force_real ? @filename : current_filename
|
||
|
||
private
|
||
case @config.dns.software
|
||
when 'bind'
|
||
output = []
|
||
begin
|
||
IO.popen("named-checkzone -i #{check_type} '#{@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 (I/O error)")
|
||
end
|
||
|
||
if $?.success?
|
||
serial = nil
|
||
messages = []
|
||
output.each do |l|
|
||
next if l == "OK"
|
||
if l =~ /: loaded serial (\d+)$/
|
||
serial = $1
|
||
next
|
||
end
|
||
messages << l
|
||
end
|
||
|
||
def backup_zone_filename
|
||
@filename + ".ch-backup"
|
||
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
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
Also available in: Unified diff
[evol] reworked DNS classes (refs #31)