bindg5k.rb 8.54 KB
Newer Older
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
1 2
#!/usr/bin/ruby

Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
3 4 5 6 7
if RUBY_VERSION < "2.1"
  puts "This script requires ruby >= 2.1"
  exit
end

8 9
# See also: https://www.grid5000.fr/mediawiki/index.php/DNS_server

Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
10 11
require 'pp'
require 'erb'
12
require 'pathname'
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
13 14
require '../lib/input_loader'

15
refapi = load_yaml_file_hierarchy("../../input/grid5000/")
16
$output_dir = ENV['puppet_repo'] || '/tmp/puppet-repo'
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
17

18 19 20 21 22 23 24 25 26
# Create a dns entry
# $GENERATE 1-16 graoully-$-bmc IN A 172.17.70.$
#
# $GENERATE 1-16 graoully-$ IN A 172.16.70.$
# $GENERATE 1-16 graoully-$-eth0 IN CNAME graoully-$
#
# $GENERATE 1-16 graoully-$-eth0-kavlan-3 IN A 192.168.233.$
# $GENERATE 1-16 graoully-$-kavlan-3 IN CNAME graoully-$-eth0-kavlan-3
#
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
27
def print_entry(entry)
28
  if entry[:start].nil?
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
29
    range     = ""
30 31 32 33 34 35
    hostshort = entry[:uid]
    ip        = entry[:ip]
  elsif entry[:start] == entry[:end]
    range     = ""
    hostshort = "#{entry[:uid]}-#{entry[:start]}"                             # graoully-1
    ip        = "#{entry[:ip]}.#{entry[:start]+entry[:shift]}"                # 172.16.70.1
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
36 37
  else
    range     = "$GENERATE #{entry[:start]}-#{entry[:end]} "                  # $GENERATE 1-16
38
    hostshort = "#{entry[:uid]}-$"                                    # graoully-$
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
39 40 41 42
    shift     = (entry[:shift] > 0 ? '{+' + entry[:shift].to_s  + '}' : '')   
    ip        = "#{entry[:ip]}.$#{shift}"                                     # 172.16.70.$
  end

43
  hostname  = entry[:hostsuffix] ? hostshort + entry[:hostsuffix] : hostshort # graoully-$-eth0
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
44

45 46
  if entry[:cnamesuffix]
    hostalias = hostshort + entry[:cnamesuffix]
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
47 48 49 50
    return ["#{range}#{hostalias} IN A #{ip}",
            "#{range}#{hostname} IN CNAME #{hostalias}"].join("\n")
  else
    return "#{range}#{hostname} IN A #{ip}"
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
51 52
  end 
end
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
53

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
# Examples: reverse-64.16.172.db
# 96                                 IN      PTR     opensm.nancy.grid5000.fr.
# $GENERATE       1-95    $          IN      PTR     graphene-$.nancy.grid5000.fr.
# $GENERATE       1-4     ${8,0,d}   IN      PTR     graphite-$-ib0.nancy.grid5000.fr.
def print_reverse_entry(site_uid, entry)
  if entry[:start].nil?
    range     = "#{entry[:ip].split('.')[4]}"
    hostshort = entry[:uid]
  elsif entry[:start] == entry[:end]
    range     = "#{entry[:start]+entry[:shift]}"
    hostshort = "#{entry[:uid]}-#{entry[:start]}"            # graoully-1
  else
    range     = "$GENERATE #{entry[:start]}-#{entry[:end]} " # $GENERATE 1-16
    shift     = (entry[:shift] > 0 ? '${+' + entry[:shift].to_s  + '}' : '$')
    range     += shift
    hostshort = "#{entry[:uid]}-$"                           # graoully-$
  end
  file = "#{entry[:ip].split('.')[0..2].reverse.join('.')}" # 70.16.172

  if entry[:cnamesuffix].nil?
    hostname  = entry[:hostsuffix] ? hostshort + entry[:hostsuffix] : hostshort # graoully-$-eth0
  else
    hostname  = hostshort + entry[:cnamesuffix]
  end

  return ["reverse-#{file}.db", "#{range} IN PTR #{hostname}.#{site_uid}.grid5000.fr."]

end
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
82

83
# Loop over Grid'5000 sites
84
refapi["sites"].each { |site_uid, site|
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
85
  next if site_uid != 'nancy'
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
86 87 88

  entries = {}

89 90
  ["networks", "laptops", "dom0"].each { |key|
    entries[key] ||= []
91
    
92 93 94 95 96 97 98 99 100 101 102 103
    site[key].each { |uid, node| 
      if node['network_adapters'].nil?
        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
      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}"
        end
104 105
          

106 107 108 109 110
        new_entry = {
          :uid         => uid,
          :hostsuffix  => hostsuffix, # cacahuete vs. cacahuete-eth0
          :ip          => net_hash['ip'],
        }
111
        
112 113 114 115
        entries[key] << new_entry
      }
    }
  }
116

117 118 119 120
  # PDUs
  entries['pdus'] ||= []
  site['pdus'].each { |pdu_uid, pdu|
    if pdu['ip']
121

122 123 124 125
      new_entry = {
        :uid     => pdu_uid,
        :ip      => pdu['ip']
      }
126

127
      entries['pdus'] << new_entry
128

129 130
    end
  }
131
  
132
  site.fetch("clusters").sort.each { |cluster_uid, cluster|
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
133
    #next if cluster_uid != 'griffon'
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
134

135
    cluster.fetch('nodes').each_sort_by_node_uid { |node_uid, node|
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
136
      network_adapters = {}
137

138
      # Nodes
139
      #pp node_uid
140
      node.fetch('network_adapters').each { |net_uid, net_hash|
141
        network_adapters[net_uid] = {"ip" => net_hash["ip"], "mounted" => net_hash["mounted"]} #, "network_address" => net_hash["network_address"]}
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
142
      }
143 144
      
      # Kavlan
145 146 147 148
      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}
        } 
149
      } if node['kavlan']
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
150

151 152 153 154 155
      # Mic
      if node['mic'] && node['mic']['ip']
        network_adapters['mic0'] = {"ip" => node['mic']['ip']}
      end
      
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
156
      # Group ip ranges
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
157
      network_adapters.each { |net_uid, net_hash|
158 159
        next if node['status'] && node['status']['retired'] && cluster_uid == 'graphene' && net_uid == 'eth0' # FIXME

Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
160
        next unless net_hash['ip']
161 162 163 164
        
        entries["#{cluster_uid}-#{net_uid}"] ||= []
        last_entry = entries["#{cluster_uid}-#{net_uid}"].last
        
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
165
        node_id = node_uid.to_s.split(/(\d+)/)[1].to_i # node number
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
166 167
        ip = net_hash['ip']
        ip_array = ip.split('.')
168
        
169
        if last_entry && ip == last_entry[:ip] + '.' + (node_id + last_entry[:shift]).to_s && last_entry[:end] == node_id-1 # && network_address
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
170
          last_entry[:end] += 1
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
171
        else
172 173
          
          # CNAME entries
174
          hostsuffix  = "-#{net_uid}"
175 176 177
          cnamesuffix = nil # no CNAME entry by default
          if net_hash['mounted'] && /^eth[0-9]$/.match(net_uid)
            # primary interface
178 179
            cnamesuffix = '' # CNAME enabled for primary interface
          elsif /^*-kavlan-[0-9]*$/.match(net_uid)
180
            # kavlan
181 182 183 184 185 186 187 188
            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
189 190
          end
          
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
191 192
          # new range
          new_entry = {
193
            :uid         => cluster_uid,
194
            :hostsuffix  => hostsuffix, # -eth0, -kavlan-1
195 196 197 198 199
            :cnamesuffix => cnamesuffix,   # graoully-$-, graoully-$-eth0-kavlan-1
            :start       => node_id,
            :end         => node_id,
            :ip          => ip_array[0..2].join("."),
            :shift       => ip_array[3].to_i - node_id,
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
200 201
          }
          
202
          entries["#{cluster_uid}-#{net_uid}"] << new_entry
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
203 204
        end
      }
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
205

206 207 208
    } # each nodes
    
    
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
209 210 211 212
  } # each cluster
  

  #
213
  # Output
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
214
  #
215 216
  dns = []     # output file for dns entries
  reverse = {} # one hash entry per reverse dns file
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
217

218 219
  entries.each { |type, e|
    e.each { |entry|
220 221 222 223 224
      dns << print_entry(entry) # DNS

      output_file, txt_entry = print_reverse_entry(site_uid, entry) # Reverse DNS
      reverse[output_file] ||= []
      reverse[output_file] << txt_entry
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
225 226
    }
  }
227 228 229 230

  zones_dir = Pathname("#{$output_dir}/modules/bindg5k/files/zones/#{site_uid}")
  zones_dir.mkpath()

231 232 233
  # DNS (/modules/bindg5k/files/zones/nancy.db)
  manual = site_uid + '-manual.db'
  dns.unshift("$INCLUDE #{manual}") if File.exist?(zones_dir + manual) # add include statement
234

235
  output_file = site_uid + '.db'
236
  header = ERB.new(File.read('templates/bind-header.erb')).result(binding)
237 238 239
  File.write(zones_dir + output_file, header + dns.join("\n"))

  # Reverse DNS (/modules/bindg5k/files/zones/reverse-*db)
240
  reverse.each { |output_file, output|
241 242 243 244
    header = ERB.new(File.read('templates/bind-header.erb')).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 #{manual}") if File.exist?(zones_dir + manual) # add include statement

245 246
    File.write(zones_dir + output_file, header + output.join("\n"))
  }
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
247
  
248 249 250 251 252 253 254 255 256
  # files/global/conf/global-nancy.conf
  # files/site/conf/global-nancy.conf
  ['global', 'site'].each { |dir|
    conf_dir = Pathname("#{$output_dir}/modules/bindg5k/files/#{dir}/conf")
    conf_dir.mkpath()
    
    global_file = ERB.new(File.read('templates/bind-global-site.conf.erb')).result(binding)
    File.write(conf_dir + "global-#{site_uid}.conf", global_file)
  }
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
257

258
} # each sites