From 3e6ab1b0d43422fe606f85c95ccb7d44fdfc230b Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Sat, 30 Nov 2013 19:27:48 -0600 Subject: [PATCH] Creates a dns_rr resource for managing RRs --- Modulefile | 2 +- lib/puppet/provider/dns_rr/nsupdate.rb | 126 +++++++++++++++++++++++++ lib/puppet/type/dns_rr.rb | 65 +++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 lib/puppet/provider/dns_rr/nsupdate.rb create mode 100644 lib/puppet/type/dns_rr.rb diff --git a/Modulefile b/Modulefile index 0f05874..0501a34 100644 --- a/Modulefile +++ b/Modulefile @@ -1,5 +1,5 @@ name 'inkblot/bind' -version '1.0.1' +version '2.0.0' source 'git://github.com/inkblot/puppet-bind' author 'inkblot' license 'Apache 2.0' diff --git a/lib/puppet/provider/dns_rr/nsupdate.rb b/lib/puppet/provider/dns_rr/nsupdate.rb new file mode 100644 index 0000000..e7cc9ae --- /dev/null +++ b/lib/puppet/provider/dns_rr/nsupdate.rb @@ -0,0 +1,126 @@ +require 'tempfile' + +Puppet::Type.type(:dns_rr).provide(:nsupdate) do + commands :dig => 'dig', :nsupdate => 'nsupdate' + + def initialize(value={}) + super(value) + @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 + + def rrdata=(rrdata) + @properties[:rrdata] = rrdata + end + +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] + end + + def specarray + resource[:spec].split('/') + end + + def rrclass + specarray[0] + end + + def type + specarray[1] + end + + def name + specarray[2] + end + + def query + unless @query + @query = dig("@#{server}", '-c', rrclass, '+noall', '+answer', name, type).lines.map do |line| + linearray = line.chomp.split /\t+/ + { + :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/type/dns_rr.rb b/lib/puppet/type/dns_rr.rb new file mode 100644 index 0000000..00b63d5 --- /dev/null +++ b/lib/puppet/type/dns_rr.rb @@ -0,0 +1,65 @@ +Puppet::Type.newtype(:dns_rr) do + @doc = "A Resource Record in the DNS" + + ensurable + + newparam(:spec, :namevar => true) do + desc "Class/Type/Name for the resource record" + + validate do |value| + 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 + end + type = $2 + if ( !%w(A AAAA CNAME NS MX SRV NAPTR PTR).include? type) + raise ArgumentError, "Invalid resource record type: %s" % type + end + else + raise ArgumentError, "%s must be of the form Class/Type/Name" % value + end + end + end + + newproperty(:ttl) do + desc 'Time to live of the resource record' + defaultto 43200 + + munge do |value| + Integer(value) + end + end + + newproperty(:rrdata, :array_matching => :all) do + desc 'The resource record\'s data' + + def insync?(is) + Array(is).sort == Array(@should).sort + 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 + +end