PageRenderTime 38ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/chef/cookbooks/bind9/recipes/default.rb

https://github.com/crowbar/barclamp-dns
Ruby | 380 lines | 302 code | 39 blank | 39 comment | 24 complexity | b9f1da46dda4cfc955128e5f57338956 MD5 | raw file
  1. # Copyright 2011, Dell
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. include_recipe "utils"
  16. require 'ipaddr'
  17. package "bind9" do
  18. case node[:platform]
  19. when "centos","redhat", "suse"
  20. package_name "bind"
  21. end
  22. action :install
  23. end
  24. package "bind9utils" do
  25. case node[:platform]
  26. when "centos","redhat", "suse"
  27. package_name "bind-utils"
  28. end
  29. action :install
  30. end
  31. binduser, bindgroup = case node[:platform]
  32. when "ubuntu","debian"
  33. [ "bind", "bind" ]
  34. when "centos","redhat","suse"
  35. [ "named", "named" ]
  36. end
  37. directory "/etc/bind"
  38. unless node[:dns][:master]
  39. directory "/etc/bind/slave" do
  40. owner binduser
  41. group bindgroup
  42. mode 0755
  43. action :create
  44. end
  45. end
  46. node.set[:dns][:zone_files]=Array.new
  47. def populate_soa(zone, old_zone = nil)
  48. defaults = {
  49. :admin => "support.#{node[:fqdn]}.",
  50. :ttl => "1h",
  51. :serial => Time.now.to_i,
  52. :slave_refresh => "12h",
  53. :slave_retry => "180",
  54. :slave_expire => "8w",
  55. :negative_cache => "300"
  56. }
  57. defaults.keys.each do |k|
  58. zone[k] ||= old_zone[k] unless old_zone.nil?
  59. zone[k] ||= defaults[k]
  60. end
  61. zone
  62. end
  63. def make_zone(zone)
  64. # copy over SOA records that we have not overridden
  65. populate_soa zone
  66. zonefile_entries=Array.new
  67. Chef::Log.debug "Processing zone: #{zone.inspect}"
  68. # Arrange for the forward lookup zone to be created.
  69. template "/etc/bind/db.#{zone[:domain]}" do
  70. source "db.erb"
  71. mode 0644
  72. owner "root"
  73. group "root"
  74. notifies :reload, "service[bind9]"
  75. variables(:zone => zone)
  76. only_if { node[:dns][:master] }
  77. end
  78. zonefile_entries << zone[:domain]
  79. # Arrange for reverse lookup zones to be created.
  80. # Since there is no elegant method for doing this that takes into account
  81. # CIDR or IPv6, do it the excessively ugly way and create one zone per IP.
  82. hostsprocessed={}
  83. zone[:hosts].keys.sort.each do |hostname|
  84. host=zone[:hosts][hostname]
  85. [:ip4addr, :ip6addr].each do |addr|
  86. next unless host[addr]
  87. next if hostsprocessed[host[addr]]
  88. hostsprocessed[host[addr]]=1
  89. rev_zone=Mash.new
  90. populate_soa rev_zone, zone
  91. rev_domain=IPAddr.new(host[addr]).reverse
  92. rev_zone[:domain] = rev_domain
  93. rev_zone[:nameservers] = zone[:nameservers]
  94. rev_zone[:hosts] = Mash.new
  95. rev_zone[:hosts]["#{rev_domain}."] = Mash.new
  96. rev_zone[:hosts]["#{rev_domain}."][:pointer]= if hostname == "@"
  97. "#{zone[:domain]}."
  98. else
  99. "#{hostname}.#{zone[:domain]}."
  100. end
  101. Chef::Log.debug "Processing zone: #{rev_zone.inspect}"
  102. template "/etc/bind/db.#{rev_domain}" do
  103. source "db.erb"
  104. mode 0644
  105. owner "root"
  106. group "root"
  107. notifies :reload, "service[bind9]"
  108. variables(:zone => rev_zone)
  109. only_if { node[:dns][:master] }
  110. end
  111. zonefile_entries << rev_domain
  112. end
  113. end
  114. if node[:dns][:master]
  115. master_ip = nil
  116. else
  117. master_ip = node[:dns][:master_ip]
  118. end
  119. Chef::Log.debug "Creating zone file for zones: #{zonefile_entries.inspect}"
  120. template "/etc/bind/zone.#{zone[:domain]}" do
  121. source "zone.erb"
  122. mode 0644
  123. owner "root"
  124. group "root"
  125. notifies :reload, "service[bind9]"
  126. variables(:zonefile_entries => zonefile_entries,
  127. :master_ip => master_ip)
  128. end
  129. node.set[:dns][:zone_files] << "/etc/bind/zone.#{zone[:domain]}"
  130. zonefile_entries
  131. end
  132. # Create our basic zone infrastructure.
  133. zones = Mash.new
  134. localhost_zone = Mash.new
  135. localhost_zone[:domain] = "localhost"
  136. prev_localhost_zone = node[:dns][:zones]["localhost"] rescue nil
  137. populate_soa(localhost_zone, prev_localhost_zone )
  138. localhost_zone[:nameservers] = ["#{node[:fqdn]}."]
  139. localhost_zone[:hosts] = Mash.new
  140. localhost_zone[:hosts]["@"] = Mash.new
  141. localhost_zone[:hosts]["@"][:ip4addr] = "127.0.0.1"
  142. localhost_zone[:hosts]["@"][:ip6addr] = "::1"
  143. unless prev_localhost_zone.nil?
  144. if prev_localhost_zone.to_hash != localhost_zone.to_hash
  145. localhost_zone[:serial] = Time.now.to_i
  146. end
  147. end
  148. zones["localhost"] = localhost_zone
  149. cluster_zone = Mash.new
  150. cluster_zone[:domain] = node[:dns][:domain]
  151. prev_cluster_zone = node[:dns][:zones][node[:dns][:domain]] rescue nil
  152. populate_soa(cluster_zone, prev_cluster_zone)
  153. cluster_zone[:nameservers] = ["#{node[:fqdn]}."]
  154. if node[:dns][:master] and not node[:dns][:slave_names].nil?
  155. node[:dns][:slave_names].each do |slave|
  156. cluster_zone[:nameservers] << "#{slave}."
  157. end
  158. end
  159. cluster_zone[:hosts] = Mash.new
  160. # As DHCP addresses can be re-used, we make sure to use the one node which is
  161. # the most recent; this requires two passes
  162. temporary_dhcp = {}
  163. # Get the config environment filter
  164. #env_filter = "dns_config_environment:#{node[:dns][:config][:environment]}"
  165. env_filter = "*:*" # Get all nodes for now. This is a hack around a timing issue in ganglia.
  166. # Get the list of nodes
  167. nodes = search(:node, "#{env_filter}")
  168. nodes.each do |n|
  169. n = Node.load(n.name)
  170. cname = n["crowbar"]["display"]["alias"] rescue nil
  171. cname = nil unless cname && ! cname.empty?
  172. base_name_no_net = n[:fqdn].chomp(".#{node[:dns][:domain]}")
  173. alias_name_no_net = cname unless base_name_no_net == cname
  174. Chef::Recipe::Barclamp::Inventory.list_networks(n).each do |network|
  175. next unless network.address
  176. if network.name == "admin"
  177. base_name = base_name_no_net
  178. alias_name = alias_name_no_net
  179. else
  180. net_name = network.name.gsub('_','-')
  181. base_name = "#{net_name}.#{base_name_no_net}"
  182. alias_name = "#{net_name}.#{alias_name_no_net}" if alias_name_no_net
  183. end
  184. cluster_zone[:hosts][base_name] = Mash.new
  185. cluster_zone[:hosts][base_name][:ip4addr] = network.address
  186. cluster_zone[:hosts][base_name][:alias] = alias_name if alias_name
  187. end
  188. # Also set DNS name with temporary DHCP address for discovered nodes
  189. if n[:state] == "discovered" &&
  190. !cluster_zone[:hosts].key?(base_name_no_net) &&
  191. Chef::Recipe::Barclamp::Inventory.get_network_by_type(n, "admin").nil?
  192. address = n[:ipaddress]
  193. time = n[:ohai_time]
  194. use_temporary = true
  195. unless temporary_dhcp[address].nil?
  196. temp_time, _temp_base_name, _temp_alias_name = temporary_dhcp[address]
  197. use_temporary = false if time < temp_time
  198. end
  199. if use_temporary
  200. temporary_dhcp[address] = [time, base_name_no_net, alias_name_no_net]
  201. end
  202. end
  203. end
  204. temporary_dhcp.each_pair do |address, value|
  205. _, base_name, alias_name = value
  206. cluster_zone[:hosts][base_name] = Mash.new
  207. cluster_zone[:hosts][base_name][:ip4addr] = address
  208. cluster_zone[:hosts][base_name][:alias] = alias_name if alias_name
  209. end
  210. # let's create records for allocated addresses which do not belong to a node
  211. search(:crowbar, "id:*_network").each do |network|
  212. #this is not network, or at least there is no nodes
  213. next unless network.has_key?("allocated_by_name")
  214. net_name=network[:id].gsub(/_network$/, '').gsub('_','-')
  215. network[:allocated_by_name].each_key do |host|
  216. if search(:node, "fqdn:#{host}").size > 0 or not host.match(/.#{node[:dns][:domain]}$/)
  217. #this is node in crowbar terms or it not belong to our domain, so lets skip it
  218. next
  219. end
  220. base_name=host.chomp(".#{node[:dns][:domain]}")
  221. unless net_name == "admin"
  222. base_name="#{net_name}.#{base_name}"
  223. end
  224. cluster_zone[:hosts][base_name] = Mash.new
  225. cluster_zone[:hosts][base_name][:ip4addr] = network[:allocated_by_name][host][:address]
  226. end
  227. end
  228. if node[:dns][:records].nil?
  229. cluster_zone[:records] = {}
  230. else
  231. # we do not want a reference to the chef attribute (since we will save this as an attribute)
  232. cluster_zone[:records] = node[:dns][:records].to_hash
  233. end
  234. unless prev_cluster_zone.nil?
  235. if prev_cluster_zone.to_hash != cluster_zone.to_hash
  236. cluster_zone[:serial] = Time.now.to_i
  237. end
  238. end
  239. zones[node[:dns][:domain]] = cluster_zone
  240. case node[:platform]
  241. when "redhat","centos"
  242. template "/etc/sysconfig/named" do
  243. source "redhat-sysconfig-named.erb"
  244. mode 0644
  245. owner "root"
  246. variables :options => { "OPTIONS" => "-c /etc/bind/named.conf" }
  247. end
  248. when "suse"
  249. template "/etc/sysconfig/named" do
  250. source "suse-sysconfig-named.erb"
  251. mode 0644
  252. owner "root"
  253. variables :options => { "NAMED_ARGS" => "-c /etc/bind/named.conf" }
  254. end
  255. end
  256. service "bind9" do
  257. case node[:platform]
  258. when "centos","redhat","suse"
  259. service_name "named"
  260. end
  261. supports :restart => true, :status => true, :reload => true
  262. running true
  263. enabled true
  264. action :enable
  265. end
  266. # Load up our default zones. These never change.
  267. if node[:dns][:master]
  268. files=%w{db.0 db.255 named.conf.default-zones}
  269. master_ip = nil
  270. else
  271. files=%w{named.conf.default-zones}
  272. master_ip = node[:dns][:master_ip]
  273. end
  274. files.each do |file|
  275. template "/etc/bind/#{file}" do
  276. source "#{file}.erb"
  277. mode 0644
  278. owner "root"
  279. group bindgroup
  280. variables(:master_ip => master_ip)
  281. notifies :reload, "service[bind9]"
  282. end
  283. end
  284. # If we don't have a local named.conf.local, create one.
  285. # We keep this around to let local users add stuff to
  286. # DNS that Crowbar will not manage.
  287. bash "/etc/bind/named.conf.local" do
  288. code "touch /etc/bind/named.conf.local"
  289. not_if { ::File.exists? "/etc/bind/named.conf.local" }
  290. end
  291. # Write out the zone databases that Crowbar will be responsible for.
  292. entries = ["0", "255"] # see default zones that never change
  293. zones.keys.sort.each do |zone|
  294. entries << make_zone(zones[zone])
  295. end
  296. # Drop files that are not used anymore (forgotten nodes, deallocated IP, etc.)
  297. entries.flatten!
  298. db_files = Dir.glob("/etc/bind/db.*")
  299. db_files.each do |db_file|
  300. entry = db_file["/etc/bind/db.".length..-1]
  301. next if entries.include?(entry)
  302. file db_file do
  303. action :delete
  304. end
  305. end
  306. # Update named.conf.crowbar to include the new zones.
  307. template "/etc/bind/named.conf.crowbar" do
  308. source "named.conf.crowbar.erb"
  309. mode 0644
  310. owner "root"
  311. group bindgroup
  312. variables(:zonefiles => node[:dns][:zone_files])
  313. notifies :reload, "service[bind9]"
  314. end
  315. if node[:dns][:master]
  316. allow_transfer = node[:dns][:allow_transfer].to_a + node[:dns][:slave_ips].to_a
  317. allow_transfer = allow_transfer.uniq.sort.compact.delete_if {|n| n.empty? }
  318. else
  319. allow_transfer = []
  320. end
  321. # We would like to bind service only to ip address from admin network
  322. admin_addr = Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, "admin").address
  323. # Rewrite our default configuration file
  324. template "/etc/bind/named.conf" do
  325. source "named.conf.erb"
  326. mode 0644
  327. owner "root"
  328. group bindgroup
  329. variables(:forwarders => node[:dns][:forwarders],
  330. :allow_transfer => allow_transfer,
  331. :ipaddress => admin_addr)
  332. notifies :restart, "service[bind9]", :immediately
  333. end
  334. execute "reload nscd after dns config change" do
  335. command "nscd -i hosts"
  336. action :nothing
  337. subscribes :run, "template[/etc/bind/db.#{node[:dns][:domain]}]"
  338. only_if { File.exist?("/var/run/nscd/nscd.pid") }
  339. end
  340. node.set[:dns][:zones]=zones
  341. include_recipe "resolver"