oar-properties.rb 5.97 KB
Newer Older
1 2
#!/usr/bin/ruby

3 4
# Generator for the OAR properties

5 6 7 8 9 10
require 'pp'
require 'erb'
require 'fileutils'
require 'pathname'
require 'json'
require 'time'
11 12 13 14
require 'yaml'
require 'hashdiff'
require 'optparse'
require 'net/ssh'
15

16
require '../oar-properties/lib/lib-oar-properties'
17 18
require '../lib/input_loader'

19
options = {}
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
20
options[:sites] = %w{grenoble lille luxembourg lyon nancy nantes reims rennes sophia}
21
options[:diff]  = false
22

23 24
OptionParser.new do |opts|
  opts.banner = "Usage: oar-properties.rb [options]"
25

26 27 28 29 30 31 32
  opts.separator ""
  opts.separator "Example: ruby oar-properties.rb -v -s nancy -d oarnodes-%s.yaml -o cmd-%s.sh"

  opts.separator ""
  opts.separator "Filters:"

  ###
33

34 35
  opts.on('-s', '--sites a,b,c', Array, 'Select site(s)',
                                        "Default: "+options[:sites].join(", ")) do |s|
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
36
    raise "Wrong argument for -s option." unless (s - options[:sites]).empty?
37
    options[:sites] = s
38
  end
39 40 41

  opts.on('-c', '--clusters a,b,c', Array, 'Select clusters(s). Default: all') do |s|
    options[:clusters] = s
42 43
  end

44 45
  opts.on('-n', '--nodes a,b,c', Array, 'Select nodes(s). Default: all') do |n|
    options[:nodes] = n
46 47
  end

48
  ###
49

50 51
  opts.separator ""
  opts.separator "Output options:"
52

Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
53
  opts.on('-o FILE', '--output=FILE', 'Output oarnodesetting command into a file. Default: stdout') do |o|
54 55
    options[:output] = o
  end
56

57 58 59 60 61 62 63 64 65 66 67 68 69
  opts.on('-e', '--exec', 'Directly apply the changes to the OAR server') do |e|
    options[:exec] = e
  end
  
  opts.on("-d", "--diff [YAML filename]", 
          "Only generates the minimal list of commands needed to update the site configuration",
          "The optional YAML file is suppose to be the output of the 'oarnodes -Y' command.",
          "If the file does not exist, the script will get the data from the OAR server and save the result on disk for future use.",
          "If no filename is specified, the script will simply connect to the OAR server.",
          "You can use the '%s' placeholder for 'site'. Ex: oarnodes-%s.yaml") do |d|
    d = true if d == nil
    options[:diff] = d
  end
70
    
71 72 73 74 75
  ###

  opts.separator ""
  opts.separator "Common options:"

Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
76 77 78
  opts.on("-v", "--[no-]verbose", "Run verbosely", "Multiple -v options increase the verbosity. The maximum is 3.") do |v|
    options[:verbose] ||= 0
    options[:verbose] = options[:verbose] + 1
79
  end
80 81 82 83 84 85 86 87
  
  # Print an options summary.
  opts.on_tail("-h", "--help", "Show this message") do
    puts opts
    exit
  end
end.parse!

Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
88
puts "Options: #{options}" if options[:verbose]
89

90 91 92
nodelist_properties = {} # ["ref"]  : properties from the reference-repo
                         # ["oar"]  : properties from the OAR server
                         # ["diff"] : diff between "ref" and "oar"
93 94

#
95
# Get the OAR properties from the reference-repo (["ref"])
96 97
#

98
nodelist_properties["ref"] = {}
99
global_hash = load_yaml_file_hierarchy('../../input/grid5000/')
100 101 102
options[:sites].each { |site_uid| 
  nodelist_properties["ref"][site_uid] = get_nodelist_properties(site_uid, global_hash["sites"][site_uid]) 
}
103 104

#
105
# Get the current OAR properties from the OAR scheduler (["oar"])
106 107 108
#

nodelist_properties["oar"] = {}
109 110
options[:sites].each { |site_uid| 
  nodelist_properties["oar"][site_uid] = {}
111

112 113 114 115 116 117
  # This is only needed for the -d option  
  if options[:diff]
    filename = options[:diff].is_a?(String) ? options[:diff].gsub("%s", site_uid) : nil
    nodelist_properties["oar"][site_uid] = oarcmd_get_nodelist_properties(site_uid, filename)
  end
}
118 119 120 121

#
# Diff
#
122 123 124
if options[:diff]
  header ||= false
  prev_diff = {}
125
  
126 127 128 129 130 131
  nodelist_properties["diff"] = {}
  
  nodelist_properties["ref"].each { |site_uid, site_properties| 
    nodelist_properties["diff"][site_uid] = {}

    site_properties.each_filtered_node_uid(options[:clusters], options[:nodes]) { |node_uid, node_properties_ref|
132
      
133
      node_properties_oar = nodelist_properties["oar"][site_uid][node_uid]
134
      
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
      diff      = diff_node_properties(node_properties_oar, node_properties_ref)
      diff_keys = diff.map{ |hashdiff_array| hashdiff_array[1] }
      
      nodelist_properties["diff"][site_uid][node_uid] = node_properties_ref.select { |key, value| diff_keys.include?(key) }
      
      case options[:verbose]
      when 1
        puts "#{node_uid}: #{diff_keys}"
      when 2
        # Give more details
        # puts "#{node_uid}: #{diff}"
        if !header
          header=true
          puts "Output format: ['~', 'key', 'old value', 'new value']"
        end
        if diff.size==0
          puts "  #{node_uid}: OK"
        elsif diff == prev_diff
          puts "  #{node_uid}: same as above"
        else
          puts "  #{node_uid}:"
          diff.each { |d| puts "    #{d}" } 
        end
        prev_diff = diff
      when 3
        # Even more details
        puts JSON.pretty_generate({node_uid => {"old values" => node_properties_oar, "new values" => node_properties_ref}})
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
162
      end
163 164
    }
    
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
165
  }
166
end # if options[:diff]
167 168

#
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
169
# Output commands
170
#
171 172 173 174 175 176 177 178 179 180 181 182 183 184
if options[:output]
  opt = options[:diff] ? 'diff' : 'ref'
  nodelist_properties[opt].each { |site_uid, site_properties| 
    
    options[:output] ? o = File.open(options[:output].gsub("%s", site_uid),'w') : o = $stdout.dup
    
    site_properties.each_filtered_node_uid(options[:clusters], options[:nodes]) { |node_uid, node_properties|
      o.write(oarcmd_set_node_properties(node_uid, node_properties) + "\n")
    }
    
    o.close
    
  }
end
Jérémie Gaidamour's avatar
Jérémie Gaidamour committed
185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
#
# Execute commands
#
if options[:exec]
  opt = options[:diff] ? 'diff' : 'ref'
  nodelist_properties[opt].each { |site_uid, site_properties| 
    
    puts "Connecting #{site_uid} ..."
    Net::SSH.start("oar.#{site_uid}.g5kadmin", 'g5kadmin', :keys => ['~/.ssh/id_rsa_g5kadmin.pub']) { |ssh|
    
      site_properties.each_filtered_node_uid(options[:clusters], options[:nodes]) { |node_uid, node_properties|
        cmd = oarcmd_set_node_properties(node_uid, node_properties)
        if cmd.size>0
          puts "#{cmd}" if options[:verbose]
          ssh_output = ssh.exec!('echo ' + cmd) 
          puts "#{ssh_output}" if options[:verbose]
        end
      }
    }
  }
end