summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/facter/dns_zone_serial.rb11
-rw-r--r--lib/puppet/provider/dns_record2/named.rb137
-rw-r--r--lib/puppet/provider/dns_zone2/named.rb96
-rw-r--r--lib/puppet/type/dns_record2.rb93
-rw-r--r--lib/puppet/type/dns_zone2.rb133
5 files changed, 469 insertions, 1 deletions
diff --git a/lib/facter/dns_zone_serial.rb b/lib/facter/dns_zone_serial.rb
index 6f25148..6575757 100644
--- a/lib/facter/dns_zone_serial.rb
+++ b/lib/facter/dns_zone_serial.rb
@@ -1,5 +1,13 @@
require 'resolv'
+def ensure_period(s)
+ if s[-1] == '.'
+ s
+ else
+ "#{s}."
+ end
+end
+
Facter.add(:dns_zone_serial) do
confine do
Facter::Util::Resolution.which('named-checkconf')
@@ -13,7 +21,8 @@ Facter.add(:dns_zone_serial) do
.filter { |zone| zone.last == 'master' }
zone_map = {}
zones.map do |zone|
- zone_map["#{zone[0]}."] = dns.getresource(zone[0], Resolv::DNS::Resource::IN::SOA).serial
+ z = ensure_period(zone[0])
+ zone_map[z] = dns.getresource(z, Resolv::DNS::Resource::IN::SOA).serial
end
zone_map
end
diff --git a/lib/puppet/provider/dns_record2/named.rb b/lib/puppet/provider/dns_record2/named.rb
new file mode 100644
index 0000000..61f4a96
--- /dev/null
+++ b/lib/puppet/provider/dns_record2/named.rb
@@ -0,0 +1,137 @@
+# require 'resolv'
+
+Puppet::Type.type(:dns_record2).provide(:named) do
+ def self.instances
+ objects = []
+ # `named-checkconf -l`.split("\n").each do |zone|
+ # zonename, cls, view, _ = zone.split(' ')
+ # path = `rndc zonestatus #{zonename} #{cls} #{view} 2>/dev/null | awk -F' ' '/^files:/ { print $2 }'`.strip;
+ # `named-checkzone -q -D #{zonename} /var/named/#{path} | sed 's/[[:space:]]\+/ /g'`.split("\n").each do |record|
+ # key, ttl, cls, type, *value = record.split(' ')
+ # value = value.join(' ')
+
+ # name = "#{zonename} #{cls} #{view} #{key} #{type} #{value}"
+
+ # objects << new(:name => name,
+ # :key => key,
+ # :ttl => ttl,
+ # :class => cls,
+ # :type => type,
+ # :value => value)
+ # end
+ # end
+ objects
+ end
+
+ def create
+ # print("Create\n")
+ end
+
+ def destroy
+ # print("Destroy\n")
+ end
+
+ def exists?
+ # print("record = [#{record().inspect}]\n")
+ record() != nil
+ end
+
+ def type
+ resource[:type]
+ end
+
+ def value
+ # print("Get old value (#{resource[:value]}, #{get(:value)})\n")
+ t = record()
+ case t
+ when NilClass
+ :absent
+ when String
+ t
+ else
+ "INVALID VALUE (#{t.class}, #{t})"
+ end
+ end
+
+ def value=(v)
+ # print("Set new value (#{v})\n")
+ # resource[:value] = v
+ end
+
+ def key
+ resource[:key]
+ end
+
+ # def key=(v)
+ # resource[:key] = v
+ # end
+
+ def zone
+ resource[:zone]
+ end
+
+ def full_key
+ if resource[:key] == '@'
+ resource[:zone]
+ elsif resource[:key][-1] == '.'
+ resource[:key]
+ else
+ "#{resource[:key]}.#{resource[:zone]}"
+ end
+ end
+
+ # def zone=(v)
+ # # set(:zone, v)
+ # resource[:zone] = v
+ # end
+
+ def record
+ # dns = Resolv::DNS.new(nameserver: ['localhost'])
+ # type = {
+ # A: Resolv::DNS::Resource::IN::A,
+ # AAAA: Resolv::DNS::Resource::IN::AAAA,
+ # CNAME: Resolv::DNS::Resource::IN::CNAME,
+ # HINFO: Resolv::DNS::Resource::IN::HINFO,
+ # MINFO: Resolv::DNS::Resource::IN::MINFO,
+ # MX: Resolv::DNS::Resource::IN::MX,
+ # NS: Resolv::DNS::Resource::IN::NS,
+ # PTR: Resolv::DNS::Resource::IN::PTR,
+ # SOA: Resolv::DNS::Resource::IN::SOA,
+ # TXT: Resolv::DNS::Resource::IN::TXT,
+ # WKS: Resolv::DNS::Resource::IN::WKS,
+ # }[resource[:type]]
+
+ # if type == nil
+ # nil
+ # else
+ # rs = dns.getresources(full_key, type)
+ # if rs.length == 0
+ # nil
+ # else
+ # rs[0]
+ # end
+ # end
+ lines = `named-checkzone -j -q -D #{zone} /var/named/zones/#{zone}db`
+ .split("\n")
+ .map {|line| line.gsub(/[[:space:]]+/, ' ').split(' ') }
+
+ matches = lines
+ .filter {|line|
+ line[0] == full_key and line[3].to_sym == resource[:type]
+ }
+ .map {|line| line[(4..)].join(' ') }
+
+
+ if matches.length == 0
+ nil
+ elsif matches.length == 1
+ matches[0]
+ else
+ matches
+ end
+ end
+
+ def cls
+ 'IN'
+ end
+end
diff --git a/lib/puppet/provider/dns_zone2/named.rb b/lib/puppet/provider/dns_zone2/named.rb
new file mode 100644
index 0000000..e249be6
--- /dev/null
+++ b/lib/puppet/provider/dns_zone2/named.rb
@@ -0,0 +1,96 @@
+Puppet::Type.type(:dns_zone2).provide(:named) do
+ def self.instances
+ `named-checkconf -l`.split("\n").map do |record|
+ name, cls, view, type = record.split(' ');
+ new(:name => name, :cls => cls, :view => view, :type => type)
+ end
+ end
+
+ def create(records)
+ print("Create #{resource[:name]}\n")
+ write_zone records
+ end
+
+ def destroy
+ print("Remove #{resource[:name]}\n")
+ end
+
+ def refresh(records)
+ print("Refresh #{resource[:name]}\n")
+ write_zone records
+ end
+
+ def exists?
+ # instances.find { |r| r.name == resource[:name] }
+ # resource[:ensure] == :present
+ `named-checkconf -l`
+ .split("\n")
+ .grep(/#{resource[:origin]} /)
+ .length > 0
+ end
+
+ def filename
+ "/var/named/zones/#{resource[:name]}db"
+ end
+
+ def zone_content(records)
+ content = <<~EOF
+ ; File managed by Puppet.
+ ; Local changes WILL be overwritten
+ ; File last generated #{Time.now}
+
+ $ORIGIN #{resource[:origin]}
+ $TTL #{resource[:default_ttl]}
+
+ @ #{resource[:soa_ttl]} IN SOA #{resource[:mname]} #{resource[:rname]} (
+ #{serial+1} ; serial
+ #{resource[:refresh]} ; refresh
+ #{resource[:retry]} ; retry
+ #{resource[:expire]} ; expire
+ #{resource[:negative_ttl]} ; Negative TTL
+ )
+ EOF
+
+ records
+ .filter {|r| r[:zone] == resource[:name] }
+ .group_by {|r| r[:type]}
+ .sort_by {|(type, _)|
+ # Bit of a hack, but ensures that SOA is always first,
+ # NS is after
+ # And the rest have stable order
+ {
+ SOA: 0,
+ NS: 1,
+ A: 2,
+ AAAA: 3,
+ CNAME: 4,
+ HINFO: 5,
+ MINFO: 6,
+ MX: 7,
+ PTR: 8,
+ TXT: 9,
+ WKS: 10,
+ }[type]
+ }.each{|(type, values)|
+ content += <<~EOF
+ ; #{type} Records
+ EOF
+ values.each {|val|
+ content += <<~EOF
+ #{val[:key]} #{val[:ttl]} IN #{val[:type]} #{val[:value]}
+ EOF
+ }
+ }
+ content
+ end
+
+ def write_zone(content)
+ File.open(filename, 'w') do |file|
+ file.write content
+ end
+ end
+
+ def serial
+ `rndc zonestatus #{name} | awk -F' ' '/^serial:/ { print $2 }'`.to_i
+ end
+end
diff --git a/lib/puppet/type/dns_record2.rb b/lib/puppet/type/dns_record2.rb
new file mode 100644
index 0000000..2074782
--- /dev/null
+++ b/lib/puppet/type/dns_record2.rb
@@ -0,0 +1,93 @@
+Puppet::Type.newtype(:dns_record2) do
+ @doc = %q{
+ }
+
+ #"named-checkconf -j -D -F raw $zonename $zonefile"
+
+ newproperty(:ensure) do
+ newvalue(:present) do
+ provider.create
+ end
+
+ newvalue(:absent) do
+ provider.destroy
+ end
+
+ defaultto :present
+
+ def retrieve
+ if provider.exists?
+ :present
+ else
+ :absent
+ end
+ end
+ end
+
+ def exists?
+ provider.exists?
+ end
+
+ newparam(:name) do
+ end
+
+ newproperty(:type) do
+ desc %q{
+ Resource Record type, such as A, AAAA, ...
+
+ Only a few types are supported, due to the DNS library only supporting these.
+ }
+ newvalues(:A,
+ :AAAA,
+ :CNAME,
+ :HINFO,
+ :MINFO,
+ :MX,
+ :NS,
+ :PTR,
+ :SOA,
+ :TXT,
+ :WKS,
+ )
+ end
+
+ newproperty(:value) do
+ desc %q{
+ DNS payload. For example an IP address.
+ }
+ end
+
+ newproperty(:key) do
+ desc %q{
+ DNS key.
+ Such as 'www'
+ }
+ end
+
+ newproperty(:zone) do
+ desc %q{
+ Zone this record belongs to
+ }
+
+ munge do |value|
+ if value[-1] == '.'
+ value
+ else
+ "#{value}."
+ end
+ end
+ end
+
+ newproperty(:cls) do
+ desc %q{
+ }
+ end
+
+ newproperty(:ttl) do
+ end
+
+ # autobefore('dns::zone') { value(:zone) }
+ autonotify('dns_zone2') { [value(:zone)] }
+ # autobefore('dns_zone2') { [value(:zone)] }
+ # TODO view
+end
diff --git a/lib/puppet/type/dns_zone2.rb b/lib/puppet/type/dns_zone2.rb
new file mode 100644
index 0000000..6cee6f2
--- /dev/null
+++ b/lib/puppet/type/dns_zone2.rb
@@ -0,0 +1,133 @@
+Puppet::Type.newtype(:dns_zone2, :self_refresh => true) do
+ @doc = %q{
+ }
+
+ newproperty(:ensure) do
+ newvalue(:present) do
+ provider.write_zone (resource.should_content)
+ end
+
+ # This should ideally remove the zone. This is however managed
+ # "higher" up in the puppet code.
+ # dns::init has a purge on the directory, and the named
+ # configuration file is fully managed by us.
+ # This means that decomissioning is as simple as removing the zone
+ # from the puppet environment.
+ newvalue(:absent) do
+ # provider.destroy
+ end
+
+ defaultto :present
+
+ def retrieve
+ if provider.exists?
+ :present
+ else
+ :absent
+ end
+ end
+ end
+
+ def refresh
+ provider.write_zone(should_content)
+ end
+
+ newparam(:origin, :namevar => true) do
+ munge do |value|
+ if value[-1] == '.'
+ value
+ else
+ "#{value}."
+ end
+ end
+ end
+
+ newparam(:cls) do
+ defaultto :IN
+ end
+
+ newparam(:view) do
+ defaultto '_default'
+ end
+
+ newparam(:type) do
+ newvalues(:master, :slave,
+ :mirror, :hint, :stub, :static_stub, :forward, :redirect)
+ aliasvalue :primary, :master
+ aliasvalue :secondary, :slave
+ defaultto :master
+ end
+
+
+ newparam(:default_ttl) do
+ defaultto '300'
+ end
+
+ newparam(:mname) do
+ munge do |value|
+ if value[-1] == '.'
+ value
+ else
+ "#{value}."
+ end
+ end
+ end
+
+ newparam(:rname) do
+ munge do |value|
+ if value[-1] == '.'
+ value
+ else
+ "#{value}."
+ end
+ end
+ end
+
+ newparam(:soa_ttl) do
+ end
+
+ newparam(:refresh) do
+ end
+
+ newparam(:retry) do
+ end
+
+ newparam(:expire) do
+ end
+
+ newparam(:negative_ttl) do
+ end
+
+ # List of all DNS records (at all)
+ #
+ # Should ideally be limited to records in this zone here instead of
+ # further down.
+ def records
+ catalog.resources.filter do |r|
+ r.is_a?(Puppet::Type.type(:dns_record2))
+ end
+ end
+
+ # Create the file resource for us.
+
+ # This prevents the directory purge on /var/named/zones from deleting us.
+ def generate
+ [Puppet::Type.type(:file).new({
+ ensure: :present,
+ path: provider.filename,
+ })]
+ end
+
+ def should_content
+ provider.zone_content(records)
+ end
+
+ # def eval_generate
+ # content = should_content
+ # catalog.resource("File[/var/named/zones/#{self[:name]}db]")[:content] = content
+ # # provider.refresh (resource.records)
+ # [catalog.resource("File[/var/named/zones/#{self[:name]}db]")]
+ # end
+
+ # autorequire(:file) {["/var/named/zones/#{self[:name]}db"]}
+end