bind9mgr 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/History.txt +6 -0
- data/Manifest.txt +16 -0
- data/README.txt +66 -0
- data/Rakefile +16 -0
- data/bin/bind9mgr +3 -0
- data/lib/bind9mgr.rb +15 -0
- data/lib/named_conf.rb +124 -0
- data/lib/parser.rb +121 -0
- data/lib/resource_record.rb +35 -0
- data/lib/zone.rb +194 -0
- data/spec/named_conf_spec.rb +147 -0
- data/spec/parser_spec.rb +127 -0
- data/spec/resource_record_spec.rb +36 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/zone_spec.rb +59 -0
- metadata +81 -0
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
.autotest
|
2
|
+
History.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
bin/bind9mgr
|
7
|
+
lib/bind9mgr.rb
|
8
|
+
lib/named_conf.rb
|
9
|
+
lib/zone.rb
|
10
|
+
lib/parser.rb
|
11
|
+
lib/resource_record.rb
|
12
|
+
spec/named_conf_spec.rb
|
13
|
+
spec/zone_spec.rb
|
14
|
+
spec/spec_helper.rb
|
15
|
+
spec/parser_spec.rb
|
16
|
+
spec/resource_record_spec.rb
|
data/README.txt
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= bind9mgr
|
2
|
+
|
3
|
+
== DESCRIPTION:
|
4
|
+
|
5
|
+
This gem contains some classes to manage bind9 zone files
|
6
|
+
|
7
|
+
== FEATURES/PROBLEMS:
|
8
|
+
|
9
|
+
Please look into specs
|
10
|
+
|
11
|
+
== SYNOPSIS:
|
12
|
+
|
13
|
+
bc = Bind9mgr::NamedConf.new( '/etc/bind/named.conf.local',
|
14
|
+
:main_ns => 'ns1.example.com',
|
15
|
+
:secondary_ns => 'ns2.example.com',
|
16
|
+
:support_email => 'support@example.com',
|
17
|
+
:main_server_ip => '192.168.1.1'
|
18
|
+
)
|
19
|
+
bc.load_with_zones
|
20
|
+
bc.zones # => [ existing zones ... Bind9mgr::Zone ]
|
21
|
+
bc.zones.first.records # => [ records ... Bind9mgr::ResourceRecord ]
|
22
|
+
|
23
|
+
|
24
|
+
== REQUIREMENTS:
|
25
|
+
|
26
|
+
rspec >= 2.6.0
|
27
|
+
awesome_print
|
28
|
+
Tested with Ruby 1.9.2
|
29
|
+
|
30
|
+
== INSTALL:
|
31
|
+
|
32
|
+
gem install bind9mgr
|
33
|
+
|
34
|
+
== DEVELOPERS:
|
35
|
+
|
36
|
+
After checking out the source, run:
|
37
|
+
|
38
|
+
$ rake newb
|
39
|
+
|
40
|
+
This task will install any missing dependencies, run the tests/specs,
|
41
|
+
and generate the RDoc.
|
42
|
+
|
43
|
+
== LICENSE:
|
44
|
+
|
45
|
+
(The MIT License)
|
46
|
+
|
47
|
+
Copyright (c) 2011 FIX
|
48
|
+
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
50
|
+
a copy of this software and associated documentation files (the
|
51
|
+
'Software'), to deal in the Software without restriction, including
|
52
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
53
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
54
|
+
permit persons to whom the Software is furnished to do so, subject to
|
55
|
+
the following conditions:
|
56
|
+
|
57
|
+
The above copyright notice and this permission notice shall be
|
58
|
+
included in all copies or substantial portions of the Software.
|
59
|
+
|
60
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
61
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
62
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
63
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
64
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
65
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
66
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
# Hoe.plugin :compiler
|
7
|
+
# Hoe.plugin :gem_prelude_sucks
|
8
|
+
# Hoe.plugin :inline
|
9
|
+
# Hoe.plugin :racc
|
10
|
+
# Hoe.plugin :rubyforge
|
11
|
+
|
12
|
+
Hoe.spec 'bind9mgr' do
|
13
|
+
developer('Mikhail Barablin', 'mikhail@mad-box.ru')
|
14
|
+
|
15
|
+
# self.rubyforge_name = 'bind9mgrx' # if different than 'bind9mgr'
|
16
|
+
end
|
data/bin/bind9mgr
ADDED
data/lib/bind9mgr.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.join( File.dirname(__FILE__), 'named_conf' )
|
4
|
+
require File.join( File.dirname(__FILE__), 'zone' )
|
5
|
+
require File.join( File.dirname(__FILE__), 'resource_record' )
|
6
|
+
require File.join( File.dirname(__FILE__), 'parser' )
|
7
|
+
|
8
|
+
module Bind9mgr
|
9
|
+
VERSION = '0.2.4'
|
10
|
+
|
11
|
+
ZONES_BIND_SUBDIR = 'zones_db'
|
12
|
+
|
13
|
+
KLASSES = %w{IN CH}
|
14
|
+
ALLOWED_TYPES = %w{A CNAME MX TXT PTR NS SRV SOA}
|
15
|
+
end
|
data/lib/named_conf.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Bind9mgr
|
2
|
+
# You can specify bind_location. If you do so then .add_zone method
|
3
|
+
# will generate zones with right filenames.
|
4
|
+
class NamedConf
|
5
|
+
# BIND_PATH = '/etc/bind'
|
6
|
+
# BIND_DB_PATH = BIND_PATH + '/master'
|
7
|
+
|
8
|
+
attr_accessor :file, :main_ns, :secondary_ns, :support_email, :main_server_ip, :bind_location
|
9
|
+
attr_reader :zones
|
10
|
+
|
11
|
+
def initialize( file = '' )
|
12
|
+
@file = file
|
13
|
+
@bind_location = File.dirname(file) if file.length > 1
|
14
|
+
load
|
15
|
+
end
|
16
|
+
|
17
|
+
def load
|
18
|
+
init_zones
|
19
|
+
parse File.read( File.join( @file )) if File.exists? file
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_with_zones
|
23
|
+
init_zones
|
24
|
+
parse File.read( File.join( @file )) if File.exists? file
|
25
|
+
zones.each{ |z| z.load}
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def parse content
|
30
|
+
content.scan(/(zone "(.*?)" \{.*?file\s+"(.*?)".*?\};\n)/m) do |zcontent, zone, file|
|
31
|
+
@zones.push Zone.new( zone, file,
|
32
|
+
:main_ns => @main_ns,
|
33
|
+
:secondary_ns => @secondary_ns,
|
34
|
+
:support_email => @support_email,
|
35
|
+
:main_server_ip => @main_server_ip )
|
36
|
+
end
|
37
|
+
@zones
|
38
|
+
end
|
39
|
+
|
40
|
+
def gen_conf_content
|
41
|
+
cont = '# File is under automatic control. Edit with caution.'
|
42
|
+
if @zones.size > 0
|
43
|
+
@zones.uniq.each do |zone|
|
44
|
+
cont << zone.gen_zone_entry << "\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
cont
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_conf_file
|
51
|
+
raise ArgumentError, "Conf file not specified" unless @file.kind_of? String
|
52
|
+
File.open( @file, 'w' ){|f| f.write( gen_conf_content )}
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_zones
|
56
|
+
@zones.uniq.each do |z|
|
57
|
+
z.file ||= gen_zone_file_name;
|
58
|
+
zones_subdir = File.dirname(z.file)
|
59
|
+
Dir.mkdir( zones_subdir ) unless File.exists?( zones_subdir )
|
60
|
+
z.write_db_file
|
61
|
+
end if @zones.size > 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_all
|
65
|
+
write_conf_file
|
66
|
+
write_zones
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_zone( zone_or_name, file_name = nil )
|
70
|
+
if zone_or_name.kind_of?( Zone )
|
71
|
+
raise ArgumentError, "file_name should be nil if instance of Zone supplied" unless file_name.nil?
|
72
|
+
zone = zone_or_name
|
73
|
+
elsif zone_or_name.kind_of?( String )
|
74
|
+
raise ArgumentError, "Main ns not secified" unless @main_ns
|
75
|
+
# raise ArgumentError, "Secondary ns not secified" unless @secondary_ns
|
76
|
+
raise ArgumentError, "Support email not secified" unless @support_email
|
77
|
+
raise ArgumentError, "Main server ip not secified" unless @main_server_ip
|
78
|
+
|
79
|
+
zone = Zone.new( zone_or_name,
|
80
|
+
file_name || gen_zone_file_name(zone_or_name),
|
81
|
+
:main_ns => @main_ns,
|
82
|
+
:secondary_ns => @secondary_ns,
|
83
|
+
:support_email => @support_email,
|
84
|
+
:main_server_ip => @main_server_ip,
|
85
|
+
:mail_server_ip => @mail_server_ip)
|
86
|
+
else
|
87
|
+
raise( RuntimeError, "BindZone or String instance needed")
|
88
|
+
end
|
89
|
+
|
90
|
+
del_zone! zone.origin
|
91
|
+
@zones.push zone
|
92
|
+
end
|
93
|
+
|
94
|
+
# We should remove zone enties and delete db file immidiately.
|
95
|
+
def del_zone!( origin_or_name )
|
96
|
+
founded = @zones.select{ |z| z.origin == origin_or_name || z.name == origin_or_name }
|
97
|
+
founded.each do |z|
|
98
|
+
z.load
|
99
|
+
File.delete( z.file ) if File.exists? z.file
|
100
|
+
end
|
101
|
+
# TODO refactor!
|
102
|
+
if founded.count > 0
|
103
|
+
@zones.delete_if{ |z| z.origin == origin_or_name || z.name == origin_or_name }
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO unsafe code: other zone entries can be updated!
|
107
|
+
write_conf_file
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def gen_zone_file_name( zone_name )
|
113
|
+
raise ArgumentError, "Bind location not specified" unless bind_location.kind_of?( String )
|
114
|
+
ext_zone_name = zone_name + '.db'
|
115
|
+
|
116
|
+
return ext_zone_name if bind_location.length < 1
|
117
|
+
return File.join( bind_location, ZONES_BIND_SUBDIR, ext_zone_name )
|
118
|
+
end
|
119
|
+
|
120
|
+
def init_zones
|
121
|
+
@zones = []
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
module Bind9mgr
|
2
|
+
TYPES = %w{A CNAME TXT PTR NS SRV} # SOA, MX - are different
|
3
|
+
class Parser
|
4
|
+
|
5
|
+
attr_reader :state
|
6
|
+
attr_accessor :result # we can set appropriate Zone instance here
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@state = :start
|
10
|
+
@result = Zone.new
|
11
|
+
|
12
|
+
@STATE_RULES =
|
13
|
+
[ [:start, :origin, Proc.new{ |t| t == '$ORIGIN' }],
|
14
|
+
[:origin, :start, Proc.new{ |t| set_origin t }],
|
15
|
+
[:start, :ttl, Proc.new{ |t| t == '$TTL' }],
|
16
|
+
[:ttl, :start, Proc.new{ |t| set_ttl t }],
|
17
|
+
[:start, :type, Proc.new{ |t| TYPES.include?(t) ? add_rr(nil, nil, nil, t, nil) : false }],
|
18
|
+
[:start, :klass, Proc.new{ |t| KLASSES.include?(t) ? add_rr(nil, nil, t, nil, nil) : false }],
|
19
|
+
[:start, :rttl, Proc.new{ |t| t.match(/^\d+$/) ? add_rr(nil, t, nil, nil, nil) : false }],
|
20
|
+
[:start, :owner, Proc.new{ |t| add_rr(t, nil, nil, nil, nil) }],
|
21
|
+
[:owner, :rttl, Proc.new{ |t| t.match(/^\d+$/) ? update_last_rr(nil, t, nil, nil, nil) : false }],
|
22
|
+
[:owner, :klass, Proc.new{ |t| KLASSES.include?(t) ? update_last_rr(nil, nil, t, nil, nil) : false }],
|
23
|
+
[:owner, :type, Proc.new{ |t| TYPES.include?(t) ? update_last_rr(nil, nil, nil, t, nil) : false }],
|
24
|
+
[:rttl, :klass, Proc.new{ |t| KLASSES.include?(t) ? update_last_rr(nil, nil, t, nil, nil) : false }],
|
25
|
+
[:klass, :type, Proc.new{ |t| TYPES.include?(t) ? update_last_rr(nil, nil, nil, t, nil) : false }],
|
26
|
+
[:type, :start, Proc.new{ |t| update_last_rr(nil, nil, nil, nil, t) }],
|
27
|
+
[:klass, :soa, Proc.new{ |t| t == 'SOA' ? update_last_rr(nil, nil, nil, t, nil) : false }],
|
28
|
+
[:soa, :start, Proc.new{ |t| rdata = [t] + @tokens.shift(7)
|
29
|
+
raise RuntimeError, "Zone parsing error: parentices expected in SOA record.\n#{@content}" if (rdata[2] != '(') && (@tokens.first != ')')
|
30
|
+
rdata.delete_at(2)
|
31
|
+
@result.options[:support_email] = rdata[1]
|
32
|
+
@result.options[:serial] = rdata[2]
|
33
|
+
@result.options[:refresh] = rdata[3]
|
34
|
+
@result.options[:retry] = rdata[4]
|
35
|
+
@result.options[:expiry] = rdata[5]
|
36
|
+
@result.options[:default_ttl] = rdata[6]
|
37
|
+
update_last_rr(nil, nil, nil, nil, rdata)
|
38
|
+
@tokens.shift
|
39
|
+
}],
|
40
|
+
[:klass, :mx, Proc.new{ |t| t == 'MX' ? update_last_rr(nil, nil, nil, t, nil) : false }],
|
41
|
+
[:mx, :start, Proc.new{ |t| update_last_rr(nil, nil, nil, nil, [t] + [@tokens.shift]) }]
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse str
|
46
|
+
@content = str # for debugging
|
47
|
+
@tokens = tokenize( str )
|
48
|
+
|
49
|
+
cntr = 0
|
50
|
+
while @tokens.size > 0
|
51
|
+
token = @tokens.shift
|
52
|
+
# puts "state: #{@state}, token: #{token}"
|
53
|
+
possible_edges = @STATE_RULES.select{|arr|arr[0] == @state }
|
54
|
+
raise "no possible_edges. cur_state: #{@state}" if possible_edges.count < 1
|
55
|
+
|
56
|
+
flag = false
|
57
|
+
while ( possible_edges.count > 0 ) && flag == false
|
58
|
+
current_edge = possible_edges.shift
|
59
|
+
flag = current_edge[2].call(token)
|
60
|
+
|
61
|
+
# ( puts " succ: #{@state} -> #{current_edge[1]}"; @state = current_edge[1] ) if flag
|
62
|
+
# ( puts " fail: #{@state} -> #{current_edge[1]}" ) unless flag
|
63
|
+
@state = current_edge[1] if flag
|
64
|
+
end
|
65
|
+
|
66
|
+
raise "no successful rules found. cur_state: #{@state}, token: #{token}" unless flag
|
67
|
+
cntr += 1
|
68
|
+
end
|
69
|
+
|
70
|
+
get_options
|
71
|
+
|
72
|
+
cntr # returning performed rules count. just for fun
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def tokenize str
|
78
|
+
str.gsub(/;.*$/, '').split(/\s/).select{|s|s.length > 0}
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_options
|
82
|
+
# main server ip
|
83
|
+
main_a_rr = @result.records.find{ |r|(r.owner == '@' || r.owner == @result.origin || r.owner.nil?) && r.type == 'A' }
|
84
|
+
unless main_a_rr
|
85
|
+
puts "WARNING: main A rr not found. Can't get main server ip"
|
86
|
+
else
|
87
|
+
@result.options[:main_server_ip] = main_a_rr.rdata
|
88
|
+
end
|
89
|
+
# name servers
|
90
|
+
ns_rrs = @result.records.select{ |r| (r.owner == '@' || r.owner == @result.origin || r.owner.nil?) && r.type == 'NS' }
|
91
|
+
if ns_rrs.count < 1
|
92
|
+
puts "WARNING: NS rrs not found. Can't NS servers"
|
93
|
+
else
|
94
|
+
@result.options[:main_ns] = ns_rrs[0].rdata
|
95
|
+
@result.options[:secondary_ns] = ns_rrs[1].rdata if ns_rrs.count > 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_origin val
|
100
|
+
@result.origin = val
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_ttl val
|
104
|
+
@result.default_ttl = val
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_rr owner, ttl, klass, type, rdata
|
108
|
+
@result.records ||= []
|
109
|
+
@result.records.push ResourceRecord.new( owner, ttl, klass, type, rdata )
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_last_rr owner, ttl, klass, type, rdata
|
113
|
+
@result.records.last.owner = owner if owner
|
114
|
+
@result.records.last.ttl = ttl if ttl
|
115
|
+
@result.records.last.klass = klass if klass
|
116
|
+
@result.records.last.type = type if type
|
117
|
+
@result.records.last.rdata = rdata if rdata
|
118
|
+
return true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Bind9mgr
|
2
|
+
class ResourceRecord
|
3
|
+
attr_accessor :owner, :ttl, :klass, :type, :rdata
|
4
|
+
|
5
|
+
def initialize owner = nil, ttl = nil, klass = nil, type = nil, rdata = nil
|
6
|
+
@owner = owner
|
7
|
+
@ttl = ttl
|
8
|
+
@klass = klass
|
9
|
+
@type = type
|
10
|
+
@rdata = rdata
|
11
|
+
end
|
12
|
+
|
13
|
+
def gen_rr_string
|
14
|
+
raise ArgumentError, "RR Type not specified" unless @type
|
15
|
+
raise ArgumentError, "RR Rdata not specified" unless @rdata
|
16
|
+
|
17
|
+
raise( ArgumentError, "wrong owner: #{owner.inspect}" ) if owner == 'localhost'
|
18
|
+
raise( ArgumentError, "wrong class: #{klass.inspect}" ) if !klass.nil? && !KLASSES.include?( klass )
|
19
|
+
raise( ArgumentError, "wrong type: #{type.inspect}" ) unless ALLOWED_TYPES.include?( type )
|
20
|
+
|
21
|
+
if @type == 'SOA'
|
22
|
+
cont = ''
|
23
|
+
cont << "#{@owner}\t#{@ttl}\t#{@klass}\t#{@type}\t#{rdata[0]} #{rdata[1]} (\n"
|
24
|
+
cont << "\t#{Time.now.to_i} ; serial\n"
|
25
|
+
cont << "\t#{rdata[3]} ; refresh\n"
|
26
|
+
cont << "\t#{rdata[4]} ; retry\n"
|
27
|
+
cont << "\t#{rdata[5]} ; expire\n"
|
28
|
+
cont << "\t#{rdata[6]} ; minimum\n"
|
29
|
+
cont << ")\n"
|
30
|
+
else
|
31
|
+
"#{@owner}\t#{@ttl}\t#{@klass}\t#{@type}\t#{[@rdata].flatten.join(' ')}\n"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/zone.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
module Bind9mgr
|
2
|
+
class Zone
|
3
|
+
RRClasses = ['IN', 'CH']
|
4
|
+
RRTypes = [ 'A',
|
5
|
+
'MX',
|
6
|
+
'SRV',
|
7
|
+
'CNAME',
|
8
|
+
'SOA',
|
9
|
+
'NS',
|
10
|
+
'TXT',
|
11
|
+
'PTR'
|
12
|
+
]
|
13
|
+
|
14
|
+
attr_accessor :origin, :default_ttl
|
15
|
+
attr_accessor :file, :options
|
16
|
+
attr_reader :records
|
17
|
+
|
18
|
+
def initialize( zone_name = nil, zone_db_file = nil, options = { } )
|
19
|
+
@origin = zone_name
|
20
|
+
@file = zone_db_file
|
21
|
+
@options = options
|
22
|
+
|
23
|
+
@default_ttl ||= 86400
|
24
|
+
@options[:serial] ||= 109
|
25
|
+
@options[:refresh] ||= 3600
|
26
|
+
@options[:retry] ||= 3600
|
27
|
+
@options[:expiry] ||= 604800
|
28
|
+
@options[:default_ttl] ||= 86400
|
29
|
+
|
30
|
+
clear_records
|
31
|
+
end
|
32
|
+
|
33
|
+
def name
|
34
|
+
@origin.sub(/\.$/, '')
|
35
|
+
end
|
36
|
+
|
37
|
+
def name= str
|
38
|
+
@origin = str + '.'
|
39
|
+
end
|
40
|
+
|
41
|
+
# +rrs_array+ is a array like: [[owner, ttl, klass, type, rdata], ...]
|
42
|
+
def self.validete_rrs_uniqueness( rrs_array )
|
43
|
+
array = []
|
44
|
+
rrs_array.each do |owner, ttl, klass, type, rdata|
|
45
|
+
raise( ArgumentError, "owner, type and rdata have to be unique" ) if array.include? [owner,type,rdata]
|
46
|
+
array.push [owner,type,rdata]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load
|
51
|
+
raise ArgumentError, "file not specified" unless @file
|
52
|
+
|
53
|
+
p = Parser.new
|
54
|
+
p.result = self
|
55
|
+
raise ArgumentError, "File: #{file} not found." unless File.exists?( @file )
|
56
|
+
p.parse File.read( @file )
|
57
|
+
end
|
58
|
+
|
59
|
+
# def parse content
|
60
|
+
# clear_records
|
61
|
+
# content.gsub!(/^\s+\n/, '')
|
62
|
+
|
63
|
+
# # find and remove SOA record with its comments
|
64
|
+
# soa = /([^\s]*?)\s+?(\d+?)?\s*?(#{RRClasses.join('|')})\s*?(SOA)\s+(.*?\).*?)\n/m
|
65
|
+
# arr = content.match(soa).to_a
|
66
|
+
# arr.shift
|
67
|
+
# @records['SOA'].push arr
|
68
|
+
# content.sub! soa, ''
|
69
|
+
|
70
|
+
# # remove comments and blank lines
|
71
|
+
# content.gsub!(/;.*$/, '')
|
72
|
+
# content.gsub!(/^\s+\n/, '')
|
73
|
+
|
74
|
+
# # other @Records
|
75
|
+
# rr = /^([^\s]+)\s+?(\d+?)?\s*?(#{RRClasses.join('|')})?\s*?(#{RRTypes.join('|')})\s+(.*?)$/
|
76
|
+
# content.lines.each do |l|
|
77
|
+
# if md = l.match(/\$TTL\s+(\d+)/)
|
78
|
+
# ( @default_ttl = md[1].to_i ) if md[1].to_i > 0
|
79
|
+
# elsif md = l.match(rr)
|
80
|
+
# tmp_a = md.to_a
|
81
|
+
# tmp_a.shift
|
82
|
+
# @records[tmp_a[3]].push tmp_a
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
# @records
|
86
|
+
# end
|
87
|
+
|
88
|
+
def gen_db_content
|
89
|
+
initialized?
|
90
|
+
raise ArgumentError, "default_ttl not secified" unless @default_ttl
|
91
|
+
|
92
|
+
add_default_rrs
|
93
|
+
|
94
|
+
cont = "; File is under automatic control. Edit with caution.\n"
|
95
|
+
cont << ";;; Zone #{@origin} ;;;" << "\n"
|
96
|
+
cont << "$ORIGIN #{@origin}" << "\n" if @origin
|
97
|
+
cont << "$TTL #{@default_ttl}" << "\n" if @default_ttl
|
98
|
+
cont << @records.map{ |r| r.gen_rr_string }.join
|
99
|
+
|
100
|
+
cont
|
101
|
+
end
|
102
|
+
|
103
|
+
def write_db_file
|
104
|
+
db_dir = File.dirname( @file )
|
105
|
+
raise( Errno::ENOENT, "No such dir: #{db_dir}" ) unless File.exists? db_dir
|
106
|
+
File.open( @file, 'w' ){|f| f.write( gen_db_content )}
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_default_rrs
|
110
|
+
raise ArgumentError, "Main ns not specified" unless @options[:main_ns]
|
111
|
+
raise ArgumentError, "Main server ip not specified" unless @options[:main_server_ip]
|
112
|
+
|
113
|
+
ensure_soa_rr( default_soa )
|
114
|
+
ensure_rr( ResourceRecord.new('@', nil, 'IN', 'A', @options[:main_server_ip]) )
|
115
|
+
ensure_rr( ResourceRecord.new('@', nil, 'IN', 'NS', @options[:main_ns]) )
|
116
|
+
ensure_rr( ResourceRecord.new('@', nil, 'IN', 'NS', @options[:secondary_ns]) ) if @options[:secondary_ns]
|
117
|
+
# ensure_rr( ResourceRecord.new('@', nil, 'IN', 'MX', ['90', @options[:mail_server_ip]]) )
|
118
|
+
ensure_rr( ResourceRecord.new('www', nil, nil, 'CNAME', '@') )
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_rr( owner, ttl, klass, type, rdata )
|
122
|
+
initialized?
|
123
|
+
@records.push ResourceRecord.new(owner, ttl, klass, type, rdata)
|
124
|
+
end
|
125
|
+
|
126
|
+
# removes all resourse record with specified owner and type
|
127
|
+
def remove_rr( owner, type )
|
128
|
+
raise( ArgumentError, "wrong owner" ) if owner.nil?
|
129
|
+
raise( ArgumentError, "wrong type" ) unless ALLOWED_TYPES.include? type
|
130
|
+
|
131
|
+
initialized?
|
132
|
+
|
133
|
+
@records.delete_if { |rr| (rr.owner == owner) && (rr.type == type) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def gen_zone_entry
|
137
|
+
initialized?
|
138
|
+
|
139
|
+
cont = ''
|
140
|
+
cont << %Q|
|
141
|
+
zone "#{name}" {
|
142
|
+
type master;
|
143
|
+
file "#{@file}";
|
144
|
+
allow-update { none; };
|
145
|
+
allow-query { any; };
|
146
|
+
};
|
147
|
+
|
|
148
|
+
end
|
149
|
+
|
150
|
+
def clear_records
|
151
|
+
@records = []
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def ensure_soa_rr record
|
157
|
+
cnt = @records.select{ |r| r.type == 'SOA' }.count
|
158
|
+
raise RuntimeError, "Multiple SOA detected. zone:#{@origin}" if cnt > 1
|
159
|
+
return false if cnt == 1
|
160
|
+
@records.unshift record
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
def ensure_rr record
|
165
|
+
max_rr_cnt = (record.type == 'NS' ? 2 : 1)
|
166
|
+
cnt = @records.select{ |rr| (rr.owner == record.owner) && (rr.type == record.type) }.count
|
167
|
+
raise RuntimeError, "Multiple rr with same owner+type detected. zone:#{@origin}" if cnt > max_rr_cnt
|
168
|
+
return false if cnt == max_rr_cnt
|
169
|
+
@records.push record
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
def default_soa
|
174
|
+
initialized?
|
175
|
+
raise ArgumentError, "Main ns not secified" unless @options[:main_ns]
|
176
|
+
raise ArgumentError, "Support email not secified" unless @options[:support_email]
|
177
|
+
|
178
|
+
ResourceRecord.new( '@', @options[:default_ttl], 'IN', 'SOA',
|
179
|
+
[ @origin,
|
180
|
+
@options[:support_email],
|
181
|
+
@options[:serial],
|
182
|
+
@options[:refresh],
|
183
|
+
@options[:retry],
|
184
|
+
@options[:expiry],
|
185
|
+
@options[:default_ttl]
|
186
|
+
] )
|
187
|
+
end
|
188
|
+
|
189
|
+
def initialized?
|
190
|
+
raise( ArgumentError, "zone not initialized" ) if @origin.nil? || @file.nil?
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bind9mgr::NamedConf do
|
4
|
+
before do
|
5
|
+
@example_com_db_content = %q{$TTL 86400 ; 1 day
|
6
|
+
@ IN SOA testdomain.com. admin@testdomain.com. (
|
7
|
+
1111083002 ; serial
|
8
|
+
14400 ; refresh (4 h)
|
9
|
+
3600 ; retry (1 h)
|
10
|
+
2592000 ; expire (4w2d)
|
11
|
+
600 ; minimum (10 minute)
|
12
|
+
)
|
13
|
+
@ IN NS ns.example.com.
|
14
|
+
sub1 IN A 192.168.1.2
|
15
|
+
sub2 IN A 192.168.1.3
|
16
|
+
alias1 IN CNAME ns
|
17
|
+
www CNAME @
|
18
|
+
}
|
19
|
+
|
20
|
+
@test_conf_content = %q{// test file
|
21
|
+
zone "cloud.ru" {
|
22
|
+
type master;
|
23
|
+
file "testdomain.com.db";
|
24
|
+
};
|
25
|
+
|
26
|
+
zone "1.168.192.in-addr.arpa" {
|
27
|
+
type master;
|
28
|
+
file "testdomain.com.db";
|
29
|
+
};
|
30
|
+
}
|
31
|
+
|
32
|
+
@test_db_content = %q{$ORIGIN testdomain.com.
|
33
|
+
$TTL 86400 ; 1 day
|
34
|
+
@ IN SOA testdomain.com. admin@testdomain.com. (
|
35
|
+
2011083002 ; serial
|
36
|
+
14400 ; refresh (4 h)
|
37
|
+
3600 ; retry (1 h)
|
38
|
+
2592000 ; expire (4w2d)
|
39
|
+
600 ; minimum (10 minute)
|
40
|
+
)
|
41
|
+
IN NS ns.testdomain.com.
|
42
|
+
testdomain.com. IN A 192.168.1.1
|
43
|
+
sub1 IN A 192.168.1.2
|
44
|
+
sub2 IN A 192.168.1.3
|
45
|
+
alias1 IN CNAME ns
|
46
|
+
}
|
47
|
+
|
48
|
+
File.stub(:exists?).with(anything()).and_return(false)
|
49
|
+
File.stub(:exists?).with("testfile.conf").and_return(true)
|
50
|
+
File.stub(:exists?).with("testdomain.com.db").and_return(true)
|
51
|
+
|
52
|
+
File.stub(:read).with("testfile.conf").and_return(@test_conf_content)
|
53
|
+
File.stub(:read).with("testdomain.com.db").and_return(@test_db_content)
|
54
|
+
|
55
|
+
@nc = Bind9mgr::NamedConf.new
|
56
|
+
@nc.file = 'testfile.conf'
|
57
|
+
@nc.bind_location = ''
|
58
|
+
@nc.main_ns = 'ns1.example.com'
|
59
|
+
@nc.secondary_ns = 'ns2.example.com'
|
60
|
+
@nc.support_email = 'ns1.example.com'
|
61
|
+
@nc.main_server_ip = 'ns1.example.com'
|
62
|
+
@nc.load_with_zones
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should be creatable" do
|
66
|
+
expect { Bind9mgr::NamedConf.new }.to_not raise_error
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should fail to add_zone(some_string) unless bind_location filled" do
|
70
|
+
@nc.bind_location = nil
|
71
|
+
expect { @nc.add_zone('example.com') }.to raise_error(ArgumentError)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should fail to add_zone(some_string) unless main NS name filled" do
|
75
|
+
@nc.main_ns = nil
|
76
|
+
expect { @nc.add_zone('example.com') }.to raise_error(ArgumentError)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Behaivor changed. Secondary ns absence causes warning only
|
80
|
+
# it "should fail to add_zone(some_string) unless secondary NS name filled" do
|
81
|
+
# @nc.secondary_ns = nil
|
82
|
+
# expect { @nc.add_zone('example.com') }.to raise_error(ArgumentError)
|
83
|
+
# end
|
84
|
+
|
85
|
+
it "should fail to add_zone(some_string) unless support email filled" do
|
86
|
+
@nc.support_email = nil
|
87
|
+
expect { @nc.add_zone('example.com') }.to raise_error(ArgumentError)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should fail to add_zone(some_string) unless main server ip filled" do
|
91
|
+
@nc.main_server_ip = nil
|
92
|
+
expect { @nc.add_zone('example.com') }.to raise_error(ArgumentError)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should fill @file if argument supplyed on instantiation" do
|
96
|
+
nc = Bind9mgr::NamedConf.new('testfile.conf')
|
97
|
+
nc.file.should eql 'testfile.conf'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should parse conf file on instantiation if supplyed and file exists" do
|
101
|
+
Bind9mgr::NamedConf.any_instance.should_receive(:parse).with(an_instance_of(String)).and_return(nil)
|
102
|
+
nc = Bind9mgr::NamedConf.new('testfile.conf')
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should have zones after conf file parsing" do
|
106
|
+
nc = Bind9mgr::NamedConf.new('testfile.conf')
|
107
|
+
nc.zones.count.should == 2
|
108
|
+
nc.zones[0].should be_kind_of(Bind9mgr::Zone)
|
109
|
+
nc.zones[1].should be_kind_of(Bind9mgr::Zone)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should init zones before load" do
|
113
|
+
@nc.zones.count.should == 2 # as specified in "before"
|
114
|
+
|
115
|
+
@nc.file = "wrong.file.conf"
|
116
|
+
@nc.load
|
117
|
+
@nc.zones.should be_empty()
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should have an empty array of zones on instantiation without conf file" do
|
121
|
+
nc = Bind9mgr::NamedConf.new
|
122
|
+
nc.zones.should be_empty()
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should load zones data on 'load_with_zones'" do
|
126
|
+
@nc.load_with_zones
|
127
|
+
@nc.zones.first.records.count.should > 0
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should remove old zone when zone with same name added" do
|
131
|
+
File.stub(:exists?).with("example.com.db").and_return(true) # lets think that there is no db file for this zone
|
132
|
+
File.stub(:read).with("example.com.db").and_return(@example_com_db_content)
|
133
|
+
File.stub(:delete).with("example.com.db").and_return(1)
|
134
|
+
|
135
|
+
@nc.add_zone( 'example.com' )
|
136
|
+
@nc.zones.last.add_rr( 'cname', nil, nil, 'CNAME', '@' )
|
137
|
+
@nc.zones.count.should == 3 # we specified 2 zones in "before"
|
138
|
+
@nc.add_zone( 'example.com' )
|
139
|
+
@nc.zones.last.records.count.should == 0
|
140
|
+
@nc.zones.last.add_default_rrs
|
141
|
+
@nc.zones.last.records.count.should == 5 # new zone should have SOA, A, CNAME(www) and 2*NS records
|
142
|
+
end
|
143
|
+
|
144
|
+
pending "should automatically generate file name for zone db if not supplied"
|
145
|
+
pending "should automatically make dir for zone db files"
|
146
|
+
pending "should have methods to edit SOA values"
|
147
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bind9mgr::Parser do
|
4
|
+
let(:test_zone) do
|
5
|
+
%q{$ORIGIN cloud.ru.
|
6
|
+
$TTL 86400 ; 1 day
|
7
|
+
@ IN SOA cloud.ru. root.cloud.ru. (
|
8
|
+
2011083002 ; serial
|
9
|
+
14400 ; refresh (4 h)
|
10
|
+
3600 ; retry (1 h)
|
11
|
+
2592000 ; expire (4w2d)
|
12
|
+
600 ; minimum (10 minute)
|
13
|
+
)
|
14
|
+
IN NS ns.cloud.ru.
|
15
|
+
ns IN A 192.168.1.200
|
16
|
+
nfsstorage IN CNAME ns
|
17
|
+
mail IN MX 40 192.168.1.33
|
18
|
+
www CNAME @
|
19
|
+
cloud.ru. IN A 192.168.1.1
|
20
|
+
NS ns2.cloud.ru
|
21
|
+
manager IN A 192.168.1.20
|
22
|
+
director IN A 192.168.1.23
|
23
|
+
directorproxy IN A 192.168.1.24
|
24
|
+
oracle IN A 192.168.1.19
|
25
|
+
vcenter IN A 192.168.1.12
|
26
|
+
esx1 IN A 192.168.1.2
|
27
|
+
; some comment
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
before do
|
32
|
+
p = Bind9mgr::Parser.new
|
33
|
+
p.parse test_zone
|
34
|
+
@result = p.result
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should parse test data without errors" do
|
38
|
+
expect {
|
39
|
+
p = Bind9mgr::Parser.new
|
40
|
+
p.parse test_zone
|
41
|
+
}.not_to raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should raise exception if there is no parentices in SOA definition" do
|
45
|
+
p = Bind9mgr::Parser.new
|
46
|
+
expect{
|
47
|
+
p.parse %q{
|
48
|
+
@ IN SOA cloud.ru. root.cloud.ru. (
|
49
|
+
2011083002 ; serial
|
50
|
+
14400 ; refresh (4 h)
|
51
|
+
3600 ; retry (1 h)
|
52
|
+
2592000 ; expire (4w2d)
|
53
|
+
600 ; minimum (10 minute)
|
54
|
+
|
55
|
+
}
|
56
|
+
}.to raise_error
|
57
|
+
expect{
|
58
|
+
p.parse %q{
|
59
|
+
@ IN SOA cloud.ru. root.cloud.ru.
|
60
|
+
2011083002 ; serial
|
61
|
+
14400 ; refresh (4 h)
|
62
|
+
3600 ; retry (1 h)
|
63
|
+
2592000 ; expire (4w2d)
|
64
|
+
600 ; minimum (10 minute)
|
65
|
+
)
|
66
|
+
}
|
67
|
+
}.to raise_error
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should provide Zone with ResourceRecords" do
|
71
|
+
@result.should be_kind_of Bind9mgr::Zone
|
72
|
+
@result.records.count.should > 0
|
73
|
+
@result.records.first.should be_kind_of Bind9mgr::ResourceRecord # in hope that othes wil be the same
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should have SOA record as first element of records" do
|
77
|
+
@result.records.first.type.should == 'SOA'
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should have 7 elements in rdata of SOA record" do
|
81
|
+
@result.records.first.rdata.count.should == 7
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should fill default_ttl" do
|
85
|
+
@result.default_ttl.should be
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should parse CNAME records" do
|
89
|
+
@result.records[3].type.should == 'CNAME'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should parse A records" do
|
93
|
+
rr = @result.records[2]
|
94
|
+
rr.type.should == 'A'
|
95
|
+
rr.owner.should eql('ns')
|
96
|
+
rr.klass.should eql('IN')
|
97
|
+
rr.rdata.should eql('192.168.1.200')
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should parse MX records(and mx priority!)" do
|
101
|
+
rr = @result.records[4]
|
102
|
+
rr.type.should == 'MX'
|
103
|
+
rr.owner.should eql('mail')
|
104
|
+
rr.klass.should eql('IN')
|
105
|
+
rr.rdata.should be_kind_of Array
|
106
|
+
rr.rdata.should eql(['40', '192.168.1.33'])
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should parse records without ttl and class" do
|
110
|
+
rr = @result.records[5]
|
111
|
+
rr.type.should eql('CNAME')
|
112
|
+
rr.owner.should eql('www')
|
113
|
+
rr.klass.should eql(nil)
|
114
|
+
rr.rdata.should eql('@')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should parse records after records without ttl and class" do
|
118
|
+
rr = @result.records[6]
|
119
|
+
rr.owner.should eql('cloud.ru.')
|
120
|
+
rr.type.should eql('A')
|
121
|
+
rr.klass.should eql('IN')
|
122
|
+
rr.rdata.should eql('192.168.1.1')
|
123
|
+
end
|
124
|
+
|
125
|
+
pending "should parse PTR records (12 IN PTR something.com.)" do
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bind9mgr::ResourceRecord do
|
4
|
+
before do
|
5
|
+
@rr = Bind9mgr::ResourceRecord.new
|
6
|
+
@a_string = %q{example.com. IN A 192.168.1.1}
|
7
|
+
@cname_string = %q{cname IN CNAME @
|
8
|
+
}
|
9
|
+
@soa_string = %q{@ IN SOA cloud.ru. root.cloud.ru. (
|
10
|
+
2011083002 ; serial
|
11
|
+
14400 ; refresh (4 h)
|
12
|
+
3600 ; retry (1 h)
|
13
|
+
2592000 ; expire (4w2d)
|
14
|
+
600 ; minimum (10 minute)
|
15
|
+
)
|
16
|
+
}
|
17
|
+
@mx_string = %q{example.com. IN MX 10 mail.example.com.}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be instanceable" do
|
21
|
+
expect { Bind9mgr::ResourceRecord.new }.to_not raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have methods to fill parametrs" do
|
25
|
+
@rr.should respond_to( :owner, :ttl, :type, :klass, :rdata )
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have method to generate rr string" do
|
29
|
+
@rr.should respond_to( :gen_rr_string )
|
30
|
+
end
|
31
|
+
|
32
|
+
it "shoult have a list of allowed rr types" do
|
33
|
+
Bind9mgr::ALLOWED_TYPES.should be_kind_of(Array)
|
34
|
+
Bind9mgr::ALLOWED_TYPES.count.should > 0
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/zone_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bind9mgr::Zone do
|
4
|
+
before do
|
5
|
+
@test_db_content = %q{$ORIGIN testdomain.com.
|
6
|
+
$TTL 86400 ; 1 day
|
7
|
+
@ IN SOA testdomain.com. admin@testdomain.com. (
|
8
|
+
2011083002 ; serial
|
9
|
+
14400 ; refresh (4 h)
|
10
|
+
3600 ; retry (1 h)
|
11
|
+
2592000 ; expire (4w2d)
|
12
|
+
600 ; minimum (10 minute)
|
13
|
+
)
|
14
|
+
IN NS ns.testdomain.com.
|
15
|
+
testdomain.com. IN A 192.168.1.1
|
16
|
+
sub1 IN A 192.168.1.2
|
17
|
+
sub2 IN A 192.168.1.3
|
18
|
+
alias1 IN CNAME ns
|
19
|
+
}
|
20
|
+
|
21
|
+
File.stub(:exists?).with(anything()).and_return(false)
|
22
|
+
File.stub(:exists?).with("testdomain.com.db").and_return(true)
|
23
|
+
|
24
|
+
File.stub(:read).with("testdomain.com.db").and_return(@test_db_content)
|
25
|
+
|
26
|
+
@zone = Bind9mgr::Zone.new
|
27
|
+
@zone.file = 'testdomain.com.db'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should be instanceable" do
|
31
|
+
expect{ Bind9mgr::Zone.new }.not_to raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should fill itself with data on load method call" do
|
35
|
+
@zone.load
|
36
|
+
@zone.records.count.should > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
pending "should fail to generate db file content unless mandatory options filled"
|
40
|
+
pending "should raise if wrong rr type specified"
|
41
|
+
pending "should not write repeating rrs"
|
42
|
+
it "should generate db file content" do
|
43
|
+
@zone.load
|
44
|
+
cont = @zone.gen_db_content
|
45
|
+
cont.should be_kind_of( String )
|
46
|
+
cont.match(/#{@zone.origin}/m).should be
|
47
|
+
end
|
48
|
+
pending "should generate zone entry content"
|
49
|
+
|
50
|
+
it "should add default rrs before generate db content" do
|
51
|
+
zone = Bind9mgr::Zone.new( 'example.com', 'example.com.db',
|
52
|
+
{ :main_ns => '192.168.1.1',
|
53
|
+
:secondary_ns => '192.168.1.2',
|
54
|
+
:main_server_ip => '192.168.1.3',
|
55
|
+
:support_email => 'qwe@qwe.ru'
|
56
|
+
})
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bind9mgr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.4
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mikhail Barablin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-12 00:00:00.000000000 +04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
requirement: &9505680 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2.12'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *9505680
|
26
|
+
description: This gem contains some classes to manage bind9 zone files
|
27
|
+
email:
|
28
|
+
- mikhail@mad-box.ru
|
29
|
+
executables:
|
30
|
+
- bind9mgr
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
files:
|
37
|
+
- .autotest
|
38
|
+
- History.txt
|
39
|
+
- Manifest.txt
|
40
|
+
- README.txt
|
41
|
+
- Rakefile
|
42
|
+
- bin/bind9mgr
|
43
|
+
- lib/bind9mgr.rb
|
44
|
+
- lib/named_conf.rb
|
45
|
+
- lib/zone.rb
|
46
|
+
- lib/parser.rb
|
47
|
+
- lib/resource_record.rb
|
48
|
+
- spec/named_conf_spec.rb
|
49
|
+
- spec/zone_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
- spec/parser_spec.rb
|
52
|
+
- spec/resource_record_spec.rb
|
53
|
+
- .gemtest
|
54
|
+
has_rdoc: true
|
55
|
+
homepage:
|
56
|
+
licenses: []
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --main
|
60
|
+
- README.txt
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project: bind9mgr
|
77
|
+
rubygems_version: 1.6.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: This gem contains some classes to manage bind9 zone files
|
81
|
+
test_files: []
|