diff --git a/lib/puppet/provider/dns_rr/nsupdate.rb b/lib/puppet/provider/dns_rr/nsupdate.rb index 48b5f44..fca49ea 100644 --- a/lib/puppet/provider/dns_rr/nsupdate.rb +++ b/lib/puppet/provider/dns_rr/nsupdate.rb @@ -1,6 +1,9 @@ -require 'tempfile' +require 'puppet_bind/provider/nsupdate' Puppet::Type.type(:dns_rr).provide(:nsupdate) do + + include PuppetBind::Provider::NsUpdate + commands :dig => 'dig', :nsupdate => 'nsupdate' def initialize(value={}) @@ -8,38 +11,6 @@ Puppet::Type.type(:dns_rr).provide(:nsupdate) do @properties = {} end - def exists? - !(query.empty?) - end - - def create - update do |file| - accio(file) - end - end - - def destroy - update do |file| - destructo(file) - end - end - - def flush - return if @properties.empty? - update do |file| - destructo(file) - accio(file) - end - end - - def ttl - query.first[:ttl] - end - - def ttl=(ttl) - @properties[:ttl] = ttl - end - def rrdata query.map { |record| record[:rrdata] }.sort end @@ -50,43 +21,8 @@ Puppet::Type.type(:dns_rr).provide(:nsupdate) do private - def update(&block) - file = Tempfile.new('dns_rr-nsupdate-') - file.write "server #{server}\n" - file.write "zone #{resource[:zone]}\n" unless resource[:zone].nil? - yield file - file.write "send\n" - file.close - if keyed? - nsupdate('-y', tsig_param, file.path) - else - nsupdate(file.path) - end - file.unlink - end - - def accio(file) - resource[:rrdata].each do |datum| - file.write "update add #{name}. #{resource[:ttl]} #{rrclass} #{type} #{datum}\n" - end - end - - def destructo(file) - rrdata.each do |datum| - file.write "update delete #{name}. #{ttl} #{rrclass} #{type} #{datum}\n" - end - end - - def keyed? - !(resource[:secret].nil?) - end - - def tsig_param - "#{resource[:hmac]}:#{resource[:keyname]}:#{resource[:secret]}" - end - - def server - resource[:server] + def newdata + resource[:rrdata] end def specarray @@ -105,27 +41,4 @@ private specarray[2] end - def query - unless @query - if keyed? - dig_text = dig("@#{server}", '+noall', '+answer', name, type, '-c', rrclass, '-y', tsig_param) - else - dig_text = dig("@#{server}", '+noall', '+answer', name, type, '-c', rrclass) - end - @query = dig_text.lines.map do |line| - linearray = line.chomp.split(/\s+/, 5) - { - :name => linearray[0], - :ttl => linearray[1], - :rrclass => linearray[2], - :type => linearray[3], - :rrdata => linearray[4] - } - end.select do |record| - record[:name] == "#{name}." - end - end - @query - end - end diff --git a/lib/puppet/provider/resource_record/nsupdate.rb b/lib/puppet/provider/resource_record/nsupdate.rb new file mode 100644 index 0000000..9c1465e --- /dev/null +++ b/lib/puppet/provider/resource_record/nsupdate.rb @@ -0,0 +1,44 @@ +require 'puppet_bind/provider/nsupdate' + +Puppet::Type.type(:resource_record).provide(:nsupdate) do + + include PuppetBind::Provider::NsUpdate + + commands :dig => 'dig', :nsupdate => 'nsupdate' + + def initialize(value={}) + super(value) + @properties = {} + end + + def data + query.map { |record| record[:rrdata] }.sort + end + + def data=(data) + @properties[:rrdata] = data + end + +private + + def rrdata + data + end + + def newdata + resource[:data] + end + + def rrclass + resource[:rrclass] + end + + def type + resource[:type] + end + + def name + resource[:record] + end + +end diff --git a/lib/puppet/type/dns_rr.rb b/lib/puppet/type/dns_rr.rb index 71ba733..3e9fe6b 100644 --- a/lib/puppet/type/dns_rr.rb +++ b/lib/puppet/type/dns_rr.rb @@ -10,14 +10,14 @@ Puppet::Type.newtype(:dns_rr) do if (value =~ /^([A-Z]+)\/([A-Z]+)\/[a-zA-Z0-9._-]+$/) rrclass = $1 if ( !%w(IN CH HS).include? rrclass ) - raise ArgumentError, "Invalid resource record class: %s" % rrdata + Util::Errors.fail "Invalid resource record class: %s" % rrdata end type = $2 - if ( !%w(A AAAA CNAME NS MX SRV NAPTR PTR).include? type) - raise ArgumentError, "Invalid resource record type: %s" % type + if ( !%w(A AAAA CNAME NS MX SPF SRV NAPTR PTR TXT).include? type) + Util::Errors.fail "Invalid resource record type: %s" % type end else - raise ArgumentError, "%s must be of the form Class/Type/Name" % value + Util::Errors.fail "%s must be of the form Class/Type/Name" % value end end end @@ -33,6 +33,7 @@ Puppet::Type.newtype(:dns_rr) do newproperty(:rrdata, :array_matching => :all) do desc 'The resource record\'s data' + isrequired def insync?(is) Array(is).sort == Array(@should).sort diff --git a/lib/puppet/type/resource_record.rb b/lib/puppet/type/resource_record.rb new file mode 100644 index 0000000..8fe90fc --- /dev/null +++ b/lib/puppet/type/resource_record.rb @@ -0,0 +1,72 @@ +Puppet::Type.newtype(:resource_record) do + @doc = 'A Resource Record in the Domain Name System' + + ensurable + + newparam(:title, :namevar => true) do + desc 'A unique name for the puppet resource' + end + + newparam(:rrclass) do + desc 'The record class' + defaultto 'IN' + newvalues 'IN', 'CH', 'HS' + end + + newparam(:type) do + desc 'The record type' + isrequired + newvalues 'A', 'AAAA', 'CNAME', 'NS', 'MX', 'SPF', 'SRV', 'NAPTR', 'PTR', 'TXT' + end + + newparam(:record) do + desc 'The fully-qualified record name' + isrequired + + validate do |value| + Util::Errors.fail "Invalid value for record: #{value}" unless value =~ /^([a-zA-Z0-9_-]+\.)*[a-zA-Z0-9_-]+$/ + end + end + + newparam(:zone) do + desc 'The zone to update' + end + + newparam(:server) do + desc 'The master server for the resource record' + defaultto 'localhost' + end + + newparam(:keyname) do + desc 'Keyname for the TSIG key used to update the record' + defaultto 'update' + end + + newparam(:hmac) do + desc 'The HMAC type of the update key' + defaultto 'HMAC-SHA1' + end + + newparam(:secret) do + desc 'The secret of the update key' + end + + newproperty(:ttl) do + desc 'Time to live of the resource record' + defaultto 43200 + + munge do |value| + Integer(value) + end + end + + newproperty(:data, :array_matching => :all) do + desc 'The resource record\'s data' + isrequired + + def insync?(is) + Array(is).sort == Array(@should).sort + end + end + +end diff --git a/lib/puppet_bind/provider/nsupdate.rb b/lib/puppet_bind/provider/nsupdate.rb new file mode 100644 index 0000000..67c08c7 --- /dev/null +++ b/lib/puppet_bind/provider/nsupdate.rb @@ -0,0 +1,125 @@ +require 'tempfile' + +module PuppetBind + module Provider + + def self.nsupdate_provider(type) + end + + module NsUpdate + + def exists? + !(query.empty?) + end + + def create + update do |file| + accio(file) + end + end + + def destroy + update do |file| + destructo(file) + end + end + + def flush + return if @properties.empty? + update do |file| + destructo(file) + accio(file) + end + end + + def ttl + query.first[:ttl] + end + + def ttl=(ttl) + @properties[:ttl] = ttl + end + + private + + def update(&block) + file = Tempfile.new('dns_rr-nsupdate-') + file.write "server #{server}\n" + file.write "zone #{zone}\n" unless zone.nil? + yield file + file.write "send\n" + file.close + if keyed? + nsupdate('-y', tsig_param, file.path) + else + nsupdate(file.path) + end + file.unlink + end + + def accio(file) + newdata.each do |datum| + file.write "update add #{name}. #{resource[:ttl]} #{rrclass} #{type} #{datum}\n" + end + end + + def destructo(file) + rrdata.each do |datum| + file.write "update delete #{name}. #{ttl} #{rrclass} #{type} #{datum}\n" + end + end + + def server + resource[:server] + end + + def zone + resource[:zone] + end + + def keyname + resource[:keyname] + end + + def hmac + resource[:hmac] + end + + def secret + resource[:secret] + end + + def keyed? + !secret.nil? + end + + def tsig_param + "#{hmac}:#{keyname}:#{secret}" + end + + def query + unless @query + if keyed? + dig_text = dig("@#{server}", '+noall', '+answer', name, type, '-c', rrclass, '-y', tsig_param) + else + dig_text = dig("@#{server}", '+noall', '+answer', name, type, '-c', rrclass) + end + @query = dig_text.lines.map do |line| + linearray = line.chomp.split(/\s+/, 5) + { + :name => linearray[0], + :ttl => linearray[1], + :rrclass => linearray[2], + :type => linearray[3], + :rrdata => linearray[4] + } + end.select do |record| + record[:name] == "#{name}." + end + end + @query + end + + end + end +end