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(%r{#{resource[:origin]} }) .empty? .! 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 # Sort to ensure stable order in output, actual order doesn't matter. .each do |(type, values)| content += <<~EOF ; #{type} Records EOF values.each do |val| content += <<~EOF #{val[:key]} #{val[:ttl]} IN #{val[:type]} #{val[:value]} EOF end end 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