bind9mgr 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|