diff --git a/generators/puppet/bindg5k.rb b/generators/puppet/bindg5k.rb index e471f33f90cf7455ccd4188f1b0235b5dcdb9204..6c1c9b748eae262d03b7f5f2baf40392da53d0a2 100644 --- a/generators/puppet/bindg5k.rb +++ b/generators/puppet/bindg5k.rb @@ -1,4 +1,5 @@ #!/usr/bin/ruby +# coding: utf-8 if RUBY_VERSION < "2.1" puts "This script requires ruby >= 2.1" @@ -12,15 +13,17 @@ require 'erb' require 'pathname' require 'fileutils' require 'optparse' +require 'dns/zone' require_relative '../lib/input_loader' input_data_dir = "../../input/grid5000/" refapi = load_yaml_file_hierarchy(File.expand_path(input_data_dir, File.dirname(__FILE__))) -options = {} -options[:sites] = %w{grenoble lille luxembourg lyon nancy nantes rennes sophia} -options[:output_dir] = "/tmp/puppet-repo" +$options = {} +$options[:sites] = %w{grenoble lille luxembourg lyon nancy nantes rennes sophia} +$options[:output_dir] = "/tmp/puppet-repo" +$options[:verbose] = false OptionParser.new do |opts| opts.banner = "Usage: bindg5k.rb [options]" @@ -28,16 +31,20 @@ OptionParser.new do |opts| opts.separator "" opts.separator "Example: ruby bindg5k.rb -s nancy -o /tmp/puppet-repo" - opts.on('-o', '--output-dir dir', String, 'Select the puppet repo path', "Default: " + options[:output_dir]) do |d| - options[:output_dir] = d + opts.on('-o', '--output-dir dir', String, 'Select the puppet repo path', "Default: " + $options[:output_dir]) do |d| + $options[:output_dir] = d end opts.separator "" opts.separator "Filters:" - opts.on('-s', '--sites a,b,c', Array, 'Select site(s)', "Default: " + options[:sites].join(", ")) do |s| - raise "Wrong argument for -s option." unless (s - options[:sites]).empty? - options[:sites] = s + opts.on('-s', '--sites a,b,c', Array, 'Select site(s)', "Default: " + $options[:sites].join(", ")) do |s| + raise "Wrong argument for -s option." unless (s - $options[:sites]).empty? + $options[:sites] = s + end + + opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| + $options[:verbose] = true end opts.on_tail("-h", "--help", "Show this message") do @@ -46,269 +53,440 @@ OptionParser.new do |opts| end end.parse! -$puppet_header = "; This file was generated by reference-repository.git/generators/puppet/bindg5k.rb\n; Do not edit this file by hand. Your changes will be overwritten.\n\n" - -def write_site_zones(site_uid, site, zones_dir, dns_entries) - - site_zone_content = "\n" - - dns_entries.each { |type, entries| - - next if entries["addr"].empty? - - file_name = site_uid + "-" + type + ".db" - site_zone_content += "$INCLUDE /etc/bind/zones/#{site_uid}/#{file_name}\n" - - output_file = File.join(zones_dir, file_name) - FileUtils.mkdir_p(File.dirname(output_file)) - #Don't write bond header here because those files will be included in a file that already defines it. - File.write(output_file, $puppet_header + entries["addr"].join("\n") + "\n\n", node: 'wa+') - if (! entries["cnames"].empty?) - File.write(output_file, "\n; CNAMES\n" + entries["cnames"].join("\n") + "\n\n", mode: 'a') - end - $written_files << output_file - } - - # DNS (/modules/bindg5k/files/zones/nancy.db) - manual = site_uid + '-manual.db' - if File.exist?(File.join(zones_dir, manual)) # add include statement - site_zone_content += "$INCLUDE /etc/bind/zones/#{site_uid}/#{manual}\n\n" +#Prettier aligned dump of records +class DNS::Zone::RR::A + def dump + max_pad = 30 + return "#{@label.ljust(max_pad)} IN A #{' ' * 6 + @address}" end - - output_file = site_uid + '.db' - header = ERB.new(File.read(File.expand_path('templates/bind-header.erb', File.dirname(__FILE__)))).result(binding) - FileUtils.mkdir_p(File.dirname(File.join(zones_dir, output_file))) - File.write(File.join(zones_dir, output_file), $puppet_header + header + "\n" + site_zone_content) - $written_files << File.join(zones_dir, output_file).to_s end -def write_reverse_site_zones(site_uid, zones_dir, reverse_entries) - - #Reverse DNS (/modules/bindg5k/files/zones/reverse-*db) - reverse_entries.each { |output_file, output| - header = ERB.new(File.read(File.expand_path('templates/bind-header.erb', File.dirname(__FILE__)))).result(binding) # do not move outside of the loop (it uses the output_file variable) - manual = output_file.sub('.db', '') + '-manual.db' - output.unshift("$INCLUDE /etc/bind/zones/#{site_uid}/#{manual}") if File.exist?(File.join(zones_dir, manual)) - FileUtils.mkdir_p(File.dirname(File.join(zones_dir, output_file))) - File.write(File.join(zones_dir, output_file), $puppet_header + header + output.join("\n") + "\n") - $written_files << File.join(zones_dir, output_file).to_s - } - - #Create reverse-*.db files for each reverse-*-manual.db that do not have (yet) a corresponding file. - Dir.glob(File.join("#{zones_dir}", "reverse-*-manual.db")).each { |reverse_manual_file| - reverse_manual_file = File.basename(reverse_manual_file); - output_file = reverse_manual_file.sub("-manual.db", ".db") - next if File.exists?(File.join(zones_dir, output_file)) - puts "Including orphan reverse manual file: #{reverse_manual_file}" - header = ERB.new(File.read(File.expand_path('templates/bind-header.erb', File.dirname(__FILE__)))).result(binding) - content = "$INCLUDE /etc/bind/zones/#{site_uid}/#{reverse_manual_file}" - FileUtils.mkdir_p(File.dirname(File.join(zones_dir, output_file))) - File.write(File.join(zones_dir, output_file), $puppet_header + header + "\n" + content + "\n") - $written_files << File.join(zones_dir, output_file).to_s - } +class DNS::Zone::RR::CNAME + def dump + max_pad = 30 + return @label.ljust(max_pad) + " IN CNAME " + ' ' * 6 + @domainname + end end -# files/global/conf/global-nancy.conf -# files/site/conf/global-nancy.conf -#zones_dir needed in erb -def write_global_site_conf(site_uid, dest_dir, zones_dir) - - ['global', 'site'].each { |dir| - conf_file = File.join(dest_dir, "#{dir}/conf", "global-#{site_uid}.conf") - FileUtils.mkdir_p(File.dirname(conf_file)) - conf_content = ERB.new(File.read(File.expand_path('templates/bind-global-site.conf.erb', File.dirname(__FILE__)))).result(binding) - File.write(conf_file, conf_content) - } +class DNS::Zone::RR::NS + def dump + max_pad = 30 + return @label.ljust(max_pad) + " IN NS " + ' ' * 6 + @nameserver + end end -# Create a dns entry -def print_entry(entry) - max_pad = 30 #Sane default for maximum hostname length - addr = [] - cnames = [] - hostshort = entry[:uid] - hostshort += "-" + entry[:node_uid].to_s if entry[:node_uid] - ip = entry[:ip] - hostname = entry[:hostsuffix] ? hostshort + entry[:hostsuffix] : hostshort # graoully-$-eth0 - - if entry[:cnamesuffix] - hostalias = hostshort + entry[:cnamesuffix] - addr << "#{hostalias.ljust(max_pad)} IN A #{' ' * 6 + ip}" - cnames << "#{hostname.ljust(max_pad)} IN CNAME #{' ' * 6 + hostalias}" - #If there are cnames defined, point cnames to defined hostname instead - hostname = hostalias - else - addr << "#{hostname.ljust(max_pad)} IN A #{' ' * 6 + ip}" +class DNS::Zone::RR::MX + def dump + max_pad = 30 + return @label.ljust(max_pad) + " MX " + @priority.to_s + " " * 6 + @exchange end - if entry[:cnames] - entry[:cnames].each{ |cname| - cnames << "#{cname.ljust(max_pad)} IN CNAME #{' ' * 6 + hostname}" - } +end + +class DNS::Zone::RR::SOA + #Keep the previous version of soa format + def dump + content = "@" + " " * 23 + "IN SOA " + content += @nameserver + " " + content += @email + " (\n" + content += " " * 32 + @serial.to_s + " ; serial (YYYYMMDDSS)\n" + content += " " * 32 + @refresh_ttl + " " * 9 + "; refresh\n" + content += " " * 32 + @retry_ttl + " " * 9 + "; retry\n" + content += " " * 32 + @expiry_ttl + " " * 9 + "; expire\n" + content += " " * 32 + @minimum_ttl + ")" + " " * 8 + "; negative caching\n" + return content end - return addr, cnames end -# Examples: reverse-64.16.172.db -# 96 IN PTR opensm.nancy.grid5000.fr. -def print_reverse_entry(site_uid, entry) - hostshort = entry[:uid] - hostshort += "-" + entry[:node_uid].to_s if entry[:node_uid] - ipsuffix = entry[:ip].split(".")[3] - file = "#{entry[:ip].split('.')[0..2].reverse.join('.')}" # 70.16.172 +class DNS::Zone + + attr_accessor :file_path + attr_accessor :site_uid + attr_accessor :header + attr_accessor :soa + attr_accessor :ns + attr_accessor :mx + attr_accessor :at + attr_accessor :include + + def get_header + content = "; This file was generated by reference-repository.git/generators/puppet/bindg5k.rb\n; Do not edit this file by hand. Your changes will be overwritten.\n" + if header + content += "$TTL 3h\n" + content += soa.dump + "\n" + content += ns.dump + "\n" + if at + content += at.dump + "\n" + end + content += mx.dump + "\n" + end + return content + end - if entry[:cnamesuffix].nil? - hostname = entry[:hostsuffix] ? hostshort + entry[:hostsuffix] : hostshort # graoully-$-eth0 - else - hostname = hostshort + entry[:cnamesuffix] + #Re-define Zone dump + def dump + last_type = "" + content = [] + if @include + content << "\n" + @include + end + @records.each { |record| + if record.type != last_type + content << "\n; #{record.type} records" + end + content << record.dump + last_type = record.type + } + return get_header() + content.join("\n") << "\n" end - return ["reverse-#{file}.db", "#{ipsuffix} IN PTR #{hostname}.#{site_uid}.grid5000.fr."] end -def get_servers_entries(site) - entries = [] - +def get_servers_records(site) + records = [] site['servers'].sort.each { |server_uid, server| next if server['network_adapters'].nil? server['network_adapters'].each { |net_uid, net| - next if net['ip'].nil? - new_entry = { - :uid => server_uid, - :hostsuffix => net_uid != 'default' ? "-#{net_uid}" : '', - :ip => net['ip'] - } + new_record = DNS::Zone::RR::A.new + new_record.address = net['ip'] + new_record.label = server_uid + new_record.label += "-#{net_uid}" if net_uid != 'default' + records << new_record + if server['alias'] - new_entry[:cnames] = server['alias'].reject{ |cname| cname.include?('.') }.map{ |cname| - net_uid == 'default' ? cname : "#{cname}-#{net_uid}" + server['alias'].reject{ |cname| cname.include?('.') }.each{ |cname| + cname_record = DNS::Zone::RR::CNAME.new + cname_record.label = cname + cname_record.label += "-#{net_uid}" if net_uid != 'default' + cname_record.domainname = server_uid + cname_record.domainname += "-#{net_uid}" if net_uid != 'default' + records << cname_record } end - entries << new_entry } } - - return entries + return records end -def get_pdus_entries(site) - entries = [] +def get_pdus_records(site) + records = [] site['pdus'].sort.each { |pdu_uid, pdu| next unless pdu['ip'] - new_entry = { - :uid => pdu_uid, - :ip => pdu['ip'] - } - entries << new_entry + new_record = DNS::Zone::RR::A.new + new_record.address = pdu['ip'] + new_record.label = pdu_uid + records << new_record } - return entries + return records end -def get_networks_entries(site, key) - entries = [] +def get_networks_records(site, key) + records = [] site[key].sort.each { |uid, node| if node['network_adapters'].nil? - puts "Warning: no network_adapters for #{uid}" + puts "Warning: no network_adapters for #{uid}" next end - eth_net_uid = node['network_adapters'].select{ |u, h| h['mounted'] && /^eth[0-9]$/.match(u) } # eth* interfaces + eth_net_uid = node['network_adapters'].select{ |u, h| h['mounted'] && /^eth[0-9]$/.match(u) } # eth* interfaces node['network_adapters'].each { |net_uid, net_hash| - hostsuffix = nil if ! eth_net_uid.include?(net_uid) && node['network_adapters'].size > 1 hostsuffix = "-#{net_uid}" + else + hostsuffix = '' end - new_entry = { - :uid => uid, - :hostsuffix => hostsuffix, # cacahuete vs. cacahuete-eth0 - :ip => net_hash['ip'], - } - entries << new_entry + new_record = DNS::Zone::RR::A.new + new_record.address = net_hash['ip'] + new_record.label = uid + hostsuffix + records << new_record } } - return entries + return records end -def get_node_entries(cluster_uid, node_uid, network_adapters) - - entries = {} +def get_node_records(cluster_uid, node_uid, network_adapters) + records = [] + network_adapters.each { |net_uid, net_hash| next unless net_hash['ip'] node_id = node_uid.to_s.split(/(\d+)/)[1].to_i # node number - ip = net_hash['ip'] - hostsuffix = "-#{net_uid}" - cnamesuffix = nil # no CNAME entry by default + new_record = DNS::Zone::RR::A.new + new_record.address = net_hash['ip'] + new_record.label = "#{cluster_uid}-#{node_id}" + new_record.label += "-#{net_uid}" unless net_hash['mounted'] && /^eth[0-9]$/.match(net_uid) + records << new_record if net_hash['mounted'] && /^eth[0-9]$/.match(net_uid) - #primary interface - cnamesuffix = '' # CNAME enabled for primary interface (node-id-iface cname node-id) - elsif /^*-kavlan-[0-9]*$/.match(net_uid) - # kavlan - net_primaries = network_adapters.select{ |u, h| h['mounted'] && /^eth[0-9]$/.match(u) } # list of primary interfaces - net_uid_eth, net_uid_kavlan = net_uid.to_s.scan(/^([^-]*)-(.*)$/).first # split 'eth0-kavlan-1' - - # CNAME only for primary interface kavlan - if net_primaries.include?(net_uid_eth) - hostsuffix = "-#{net_uid_kavlan}" # -kavlan-1 - cnamesuffix = "-#{net_uid}" # -eth0-kavlan-1 - end + # CNAME enabled for primary interface (node-id-iface cname node-id) + cname_record = DNS::Zone::RR::CNAME.new + cname_record.label = "#{cluster_uid}-#{node_id}-#{net_uid}" + cname_record.domainname = "#{cluster_uid}-#{node_id}" + records << cname_record end - new_entry = { - :uid => cluster_uid, - :hostsuffix => hostsuffix, # -eth0, -kavlan-1 - :cnamesuffix => cnamesuffix, # graoully-$-, graoully-$-eth0-kavlan-1 - :node_uid => node_id, - :ip => ip - } + #Handle interface aliases if (net_hash["alias"]) - new_entry[:cnames] ||= [] net_hash["alias"].each { |cname| - new_entry[:cnames] << "#{cluster_uid}-#{node_id}-#{cname}" + cname_record = DNS::Zone::RR::CNAME.new + cname_record.label = "#{cluster_uid}-#{node_id}-#{cname}" + cname_record.domainname = "#{cluster_uid}-#{node_id}" + records << cname_record } end - #Group entries by cluster and cluster-kavlan - if (/kavlan/.match(net_uid)) - entries["#{cluster_uid}-kavlan"] ||= [] - entries["#{cluster_uid}-kavlan"] << new_entry - else - entries["#{cluster_uid}"] ||= [] - entries["#{cluster_uid}"] << new_entry + } #each network adapters + return records +end + +def get_node_kavlan_records(cluster_uid, node_uid, network_adapters, kavlan_adapters) + records = [] + + kavlan_adapters.each { |net_uid, net_hash| + + next unless net_hash['ip'] + + net_primaries = network_adapters.select{ |u, h| h['mounted'] && /^eth[0-9]$/.match(u) } # list of primary interfaces + net_uid_eth, net_uid_kavlan = net_uid.to_s.scan(/^([^-]*)-(.*)$/).first # split 'eth0-kavlan-1' + + new_record = DNS::Zone::RR::A.new + new_record.address = net_hash['ip'] + new_record.label = "#{node_uid}-#{net_uid}" #sol-23-eth0-kavlan-1 + records << new_record + + # CNAME only for primary interface kavlan + if net_primaries.include?(net_uid_eth) + cname_record = DNS::Zone::RR::CNAME.new + cname_record.label = "#{node_uid}-#{net_uid_kavlan}" + cname_record.domainname = "#{node_uid}-#{net_uid}" #sol-23-eth0-kavlan-1 + records << cname_record end } #each network adapters - return entries + + return records end -puts "Writing DNS configuration files to: #{options[:output_dir]}" -puts "For site(s): #{options[:sites].join(', ')}" +def get_reverse_record(record, site_uid) + + return unless record.is_a?(DNS::Zone::RR::A) + + ip_array = record.address.split(".") + file_name = "reverse-#{ip_array[0..2].reverse.join('.')}.db" # 70.16.172 + + if /.*-kavlan-[1-3]$/.match(record.label) + #A filter in bind-global-site.conf.erb prevents entries in 'local' directory to be included in global configuration + #TODO later, also add DMZ IPs check here + file_name.prepend("local/") + end + + reverse_record = DNS::Zone::RR::PTR.new + reverse_record.label = ip_array[3] #ip suffix + reverse_record.name = "#{record.label}.#{site_uid}.grid5000.fr." + + return file_name, reverse_record +end + +def sort_records(records) + sorted_records = [] + cnames = [] + in_a = [] + ptr = [] + + records.each{ |record| + if (record.is_a?(DNS::Zone::RR::A)) + in_a << record + elsif (record.is_a?(DNS::Zone::RR::CNAME)) + cnames << record + elsif (record.is_a?(DNS::Zone::RR::PTR)) + ptr << record + end + } + in_a.sort_by!{ |record| + record.address.split('.').map{ |octet| + octet.to_i + } + } + sorted_records += in_a + ptr.sort_by!{ |record| + record.name + } + sorted_records += ptr + #Sort CNAMES by node_id for node, node_id then kavlan number for node kavlan entry or finally by label + cnames.sort_by!{ |record| + sort_by = record.label + label_array = record.label.split("-") + if label_array.length == 4 + if label_array[1].to_i != 0 && label_array[3].to_i != 0 + sort_by = [label_array[3].to_i, label_array[1].to_i] + end + elsif label_array.length > 1 + if label_array[1].to_i != 0 + sort_by = label_array[1].to_i + end + end + sort_by + } + sorted_records += cnames + return sorted_records +end + +def include_manual_file(zone) + manual_file_path = File.join(File.dirname(zone.file_path), File.basename(zone.file_path).sub('.db', '') + '-manual.db') + if (File.exists?(manual_file_path)) + return "$INCLUDE /etc/bind/zones/#{zone.site_uid}/#{File.basename(zone.file_path).sub('.db', '') + '-manual.db'}\n" + end + return '' +end + +# +def load_zone(zone_file_path, site_uid, site, header) + if File.exists?(zone_file_path) + zone = DNS::Zone.load(File.read(zone_file_path)) + else + zone = DNS::Zone.new + end + zone.site_uid = site_uid + zone.file_path = zone_file_path + #If the target file will have a header or not (manage included files without records duplication) + zone.header = header + zone.soa = zone.records[0] if zone.records.any? && zone.records[0].type == 'SOA' + + #We only want the zone to manage these records, we will manage SOA, MX, NS, @s manually + zone.records.reject! { |rec| + !( rec.is_a?(DNS::Zone::RR::A) || rec.is_a?(DNS::Zone::RR::CNAME) || rec.is_a?(DNS::Zone::RR::PTR)) || rec.label == "@" + } + if header + set_zone_header_records(zone, site) + end + zone.include = include_manual_file(zone) + return zone +end + +def set_zone_header_records(zone, site) + if zone.soa.nil? + soa = DNS::Zone::RR::SOA.new + soa.serial = Time.now.utc.strftime("%Y%m%d00") + soa.refresh_ttl = "4h" + soa.retry_ttl = "1h" + soa.expiry_ttl = "4w" + soa.minimum_ttl = "1h" + soa.nameserver = "dns.grid5000.fr." + soa.email = "nsmaster.dns.grid5000.fr." + zone.soa = soa + end + zone.ns = DNS::Zone::RR::NS.new + zone.ns.nameserver = "dns.grid5000.fr." + zone.mx = DNS::Zone::RR::MX.new + zone.mx.priority = 10 + zone.mx.exchange = "mail.#{zone.site_uid}.grid5000.fr." + if (File.basename(zone.file_path) == "#{zone.site_uid}.db") + zone.at = DNS::Zone::RR::A.new + zone.at.address = site['frontend_ip'] + end +end + +def update_serial(serial) + date_serial = DateTime.strptime(serial.to_s, "%Y%m%d%S") + now_date = DateTime.now + if (date_serial.strftime("%Y%m%d") == now_date.strftime("%Y%m%d")) + date_serial = date_serial + Rational(1, 86400) #+1 second + return date_serial.strftime("%Y%m%d%S") + else + return Time.now.utc.strftime("%Y%m%d00") + end +end + +#Check if there are differences between existing and newly created records +#We check only A and CNAME records +def diff_zone_file(zone, records) + #Compare dumped strings directly instead of RR objects + zone_records = zone.records.map{ |rec| + rec.dump + } + recs = records.map{ |rec| + rec.dump + } + removed_records = zone_records - recs + added_records = recs - zone_records + if $options[:verbose] + if removed_records.any? + puts "Removed records in zone file: #{zone.file_path}" + removed_records.each{ |rec| + puts rec + } + end + if added_records.any? + puts "Added records in zone file: #{zone.file_path}" + added_records.each{ |rec| + puts rec + } + end + end + return added_records.any? || removed_records.any? +end + +#Create reverse-*.db files for each reverse-*-manual.db that do not have (yet) a corresponding file. +def get_orphan_reverse_manual_zones(zones_dir, site_uid, site) + zones = [] + Dir.glob(File.join(zones_dir, "reverse-*-manual.db")).each { |reverse_manual_file| + output_file = reverse_manual_file.sub("-manual.db", ".db") + next if File.exists?(File.join(output_file)) + puts "Creating file for orphan reverse manual file: #{output_file}" if $options[:verbose] + #Creating the zone will include automatically the manual file + zone = load_zone(output_file, site_uid, site, true) + zones << zone + } + return zones +end + +def write_global_site_conf(site_uid, dest_dir, zones_dir) + ['global', 'site'].each { |dir| + conf_file = File.join(dest_dir, "#{dir}/conf", "global-#{site_uid}.conf") + FileUtils.mkdir_p(File.dirname(conf_file)) + conf_content = ERB.new(File.read(File.expand_path('templates/bind-global-site.conf.erb', File.dirname(__FILE__)))).result(binding) + File.write(conf_file, conf_content) + } +end + +def write_zone(zone) + FileUtils.mkdir_p(File.dirname(zone.file_path)) + File.write(zone.file_path, zone.dump) +end + +puts "Writing DNS configuration files to: #{$options[:output_dir]}" +puts "For site(s): #{$options[:sites].join(', ')}" + +puts "Note: if you modify *-manual.db files you will have to manually update the serial in managed db file for changes to be applied" $written_files = [] # Loop over Grid'5000 sites refapi["sites"].each { |site_uid, site| + + next unless $options[:sites].include?(site_uid) - next unless options[:sites].include?(site_uid) + dest_dir = "#{$options[:output_dir]}/modules/bindg5k/files/" + zones_dir = File.join(dest_dir, "zones/#{site_uid}") - entries = {} + site_records = {} # Servers - entries['servers'] = get_servers_entries(site) unless site['servers'].nil? + site_records['servers'] = get_servers_records(site) unless site['servers'].nil? # PDUs - entries['pdus'] = get_pdus_entries(site) unless site['pdus'].nil? + site_records['pdus'] = get_pdus_records(site) unless site['pdus'].nil? # Networks and laptops (same input format) - entries['networks'] = get_networks_entries(site, 'networks') unless site['networks'].nil? - entries['laptops'] = get_networks_entries(site, 'laptops') unless site['laptops'].nil? + site_records['networks'] = get_networks_records(site, 'networks') unless site['networks'].nil? + site_records['laptops'] = get_networks_records(site, 'laptops') unless site['laptops'].nil? site.fetch("clusters").sort.each { |cluster_uid, cluster| @@ -323,88 +501,93 @@ refapi["sites"].each { |site_uid, site| network_adapters[net_uid] = {"ip" => net_hash["ip"], "mounted" => net_hash["mounted"], "alias" => net_hash["alias"]} } + # Mic + if node['mic'] && node['mic']['ip'] + network_adapters['mic0'] = {"ip" => node['mic']['ip']} + end + + site_records[cluster_uid] ||= [] + site_records[cluster_uid] += get_node_records(cluster_uid, node_uid, network_adapters) + # Kavlan if node['kavlan'] + kavlan_adapters = {} node.fetch('kavlan').each { |net_uid, net_hash| net_hash.each { |kavlan_net_uid, ip| - network_adapters["#{net_uid}-#{kavlan_net_uid}"] = {"ip" => ip, "mounted" => nil} + kavlan_adapters["#{net_uid}-#{kavlan_net_uid}"] = {"ip" => ip, "mounted" => node['network_adapters'][net_uid]['mounted']} } } + site_records["#{cluster_uid}-kavlan"] ||= [] + site_records["#{cluster_uid}-kavlan"] += get_node_kavlan_records(cluster_uid, node_uid, network_adapters, kavlan_adapters) end - - # Mic - if node['mic'] && node['mic']['ip'] - network_adapters['mic0'] = {"ip" => node['mic']['ip']} - end - - node_entries = get_node_entries(cluster_uid, node_uid, network_adapters) - node_entries.each { |k, n_entries| - entries[k] ||= [] - entries[k] += n_entries - } } # each nodes } # each cluster - # - # Output - # - dns = {} # one hash entry per type output file (e.g: nancy/servers.db) - reverse = {} # one hash entry per reverse dns file - local_reverse_list = [] # reverse zone files that include local kavlan (kavlan-1,kavlan-2,kavlan-3). + reverse_records = {} # one hash entry per reverse dns file - entries.each { |type, e| + site_records.each { |zone, records| - dns[type] ||= {} - dns[type]["addr"] ||= [] - dns[type]["cnames"] ||= [] + #Sort records + site_records[zone] = sort_records(records) - e.sort_by! { |entry| - entry[:ip].split('.').map{ |octet| - octet.to_i - } + records.each{ |record| + #get Reverse records + reverse_file_name, reverse_record = get_reverse_record(record, site_uid) + if reverse_file_name != nil + reverse_records[reverse_file_name] ||= [] + reverse_records[reverse_file_name] << reverse_record + end } + } - e.each { |entry| + zones = [] - addr_entries, cname_entries = print_entry(entry) - dns[type]["addr"] += addr_entries - if (! cname_entries.empty?) - dns[type]["cnames"] += cname_entries - end + #Sort reverse records and create reverse zone from files + reverse_records.each{ |file_name, records| + records.sort!{ |a, b| + a.label.to_i <=> b.label.to_i + } - #Reverse entries - reverse_output_file, txt_entry = print_reverse_entry(site_uid, entry) # Reverse DNS - if /.*-kavlan-[1-3]$/.match(entry[:hostsuffix]) - reverse_output_file.prepend("local/") - local_reverse_list << reverse_output_file - end + reverse_file_path = File.join(zones_dir, file_name) + zone = load_zone(reverse_file_path, site_uid, site, true) + if diff_zone_file(zone, records) + zone.soa.serial = update_serial(zone.soa.serial) + end + zone.records = records; + zones << zone + } - reverse[reverse_output_file] ||= [] - reverse[reverse_output_file] << txt_entry - } + #Manage site zone (SITE.db file) + #It only contains header and inclusion of other db files + #Check modification in included files and update serial accordingly + site_zone_path = File.join(zones_dir, site_uid + ".db") + site_zone = load_zone(site_zone_path, site_uid, site, true) + site_zone_changed = false + + site_records.each{ |type, records| + zone_file_path = File.join(zones_dir, site_uid + "-" + type + ".db") + zone = load_zone(zone_file_path, site_uid, site, false) + if diff_zone_file(zone, records) + puts "Zone file changed: #{zone.file_path}" if $options[:verbose] + site_zone_changed = true + end + zone.records = records + site_zone.include += "$INCLUDE /etc/bind/zones/#{site_uid}/#{File.basename(zone_file_path)}\n" + zones << zone } - dest_dir = "#{options[:output_dir]}/modules/bindg5k/files/" - zones_dir = File.join(dest_dir, "zones/#{site_uid}") + if (site_zone_changed) + site_zone.soa.serial = update_serial(site_zone.soa.serial) + end + + zones << site_zone - write_site_zones(site_uid, site, zones_dir, dns) + zones += get_orphan_reverse_manual_zones(zones_dir, site_uid, site) - write_reverse_site_zones(site_uid, zones_dir, reverse) + zones.each{ |zone| + write_zone(zone) + } write_global_site_conf(site_uid, dest_dir, zones_dir) } # each sites - -# Revert changes on files where only the serial has been updated -$written_files.each { |filePath| - - Dir.chdir("#{options[:output_dir]}") { - if (Dir.exists?("./.git")) - added_deleted = `git diff --numstat #{filePath} | cut -f1-2 | tr -d "\t "`.chomp() - if (added_deleted == "11") #1 addition, 1 deletion - system("git checkout -- #{filePath}") - end - end - } - -} diff --git a/generators/puppet/templates/bind-header.erb b/generators/puppet/templates/bind-header.erb deleted file mode 100644 index 948bf0fd17cdcbce8503c226444f263161833ad5..0000000000000000000000000000000000000000 --- a/generators/puppet/templates/bind-header.erb +++ /dev/null @@ -1,12 +0,0 @@ -$TTL 3h -@ IN SOA dns.grid5000.fr. nsmaster.dns.grid5000.fr. ( - <%= Time.new().strftime("%Y%m%d") %>00 ; serial (YYYYMMDDSS) - 4h ; refresh - 1h ; retry - 4w ; expire - 1h) ; negative caching - -@ IN NS dns.grid5000.fr.<% -if output_file == "#{site_uid}.db" %> -@ IN A <%= site['frontend_ip'] %><% end %> -@ MX 10 mail.<%= site_uid %>.grid5000.fr.