Une MAJ de sécurité est nécessaire sur notre version actuelle. Elle sera effectuée lundi 02/08 entre 12h30 et 13h. L'interruption de service devrait durer quelques minutes (probablement moins de 5 minutes).

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
# 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?
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
60
    range     = "#{entry[:ip].split('.')[3]}"
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    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