diff --git a/Rakefile b/Rakefile index d963e057c87ba7603f0a3ada6b243f5a21916171..17a93d3a28d4422249fb3eb0c80823e5c509a534 100644 --- a/Rakefile +++ b/Rakefile @@ -40,7 +40,7 @@ namespace :oar do end -namespace :validators do +namespace :valid do desc "Check homogeneity of clusters" task "homogeneity" do @@ -51,10 +51,29 @@ namespace :validators do task "schema" do invoke_script "#{VALIDATORS_DIR}/yaml-input-schema-validator.rb" end + + desc "Check OAR properties. Parameters: [SITE={grenoble,...}] [CLUSTER={yeti,...}] [VERBOSE=1]" + task "oar-properties" do + options = {} + options[:verbose] = true if ENV['VERBOSE'] + if ENV['SITE'] + options[:sites] = ENV['SITE'].split(',') + else + options[:sites] = G5K_SITES + end + if ENV['CLUSTER'] + options[:clusters] = ENV['CLUSTER'].split(',') + else + options[:clusters] = [] + end + ret = RefRepo::Valid::OarProperties::check(options) + exit(ret) + end + end namespace :gen do - desc "Run wiki generator. Parameter: NAME={hardware,site_hardware,...} SITE={global,grenoble,...} DO={diff,print,update}" + desc "Run wiki generator. Parameters: NAME={hardware,site_hardware,...} SITE={global,grenoble,...} DO={diff,print,update}" task "wiki" do options = {} if ENV['SITE'] @@ -81,7 +100,8 @@ namespace :gen do puts "You must specify something to do using DO=" exit(1) end - RefRepo::Gen::Wiki::wikigen(options) + ret = RefRepo::Gen::Wiki::wikigen(options) + exit(ret) end end diff --git a/lib/refrepo.rb b/lib/refrepo.rb index 3bd6df31bd2962fe5794538d499ea2ee1c466e25..bfc066ae62bb1d79ccb7b3ee5b08de0f574b3eaa 100644 --- a/lib/refrepo.rb +++ b/lib/refrepo.rb @@ -1,8 +1,11 @@ +# pre-declare those modules here module RefRepo end module RefRepo::Gen end - +module RefRepo::Valid +end +require 'refrepo/valid/oar-properties' require 'refrepo/gen/wiki' diff --git a/lib/refrepo/utils.rb b/lib/refrepo/utils.rb new file mode 100644 index 0000000000000000000000000000000000000000..9924331b3653a130cdc0660fee245725df8ad3aa --- /dev/null +++ b/lib/refrepo/utils.rb @@ -0,0 +1,28 @@ +module RefRepo::Utils + def self.get_api_config + conf = ENV['HOME']+'/.grid5000_api.yml' + yconf = YAML::load(IO::read(conf)) rescue {} + yconf['uri'] ||= 'https://api.grid5000.fr/' + yconf['version'] ||= 'stable' + return yconf + end + + def self.get_api(path) + conf = get_api_config + if conf['username'] and conf['password'] + o = { :http_basic_authentication => [conf['username'], conf['password']] } + else + o = {} + end + d = open("#{conf['uri']}/#{conf['version']}/#{path}", o).read + return JSON::parse(d) + end +end + +# Various monkey patches +class Hash + def slice(*extract) + h2 = self.select{|key, value| extract.include?(key) } + h2 + end +end diff --git a/lib/refrepo/valid/oar-properties.rb b/lib/refrepo/valid/oar-properties.rb index 95872ed292e5e85c2cd3384f0f8c7faa27643f98..9ec6e4b98cc6925ee7965b31e4c6c64e7b6ae6fd 100755 --- a/lib/refrepo/valid/oar-properties.rb +++ b/lib/refrepo/valid/oar-properties.rb @@ -13,6 +13,7 @@ require 'hashdiff' require 'optparse' require 'net/ssh' require 'open-uri' +require 'refrepo/utils' # propriétés ignorées IGNORED_PROPERTIES=%w{chassis chunks thread} @@ -28,132 +29,94 @@ class Hash end end -def parse_command_line_parameters - options = {} - options[:sites] = %w(grenoble lille luxembourg lyon nancy nantes rennes sophia) - options[:clusters] = [] - - OptionParser.new do |opts| - opts.banner = 'Usage: oar-properties-check.rb [options]' - opts.separator '' - opts.separator 'Example: ruby oar-properties-check.rb -v -s nancy' - 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 - end - opts.on('-c', '--clusters a,b,c', Array, 'Select clusters(s). Default: all') do |s| - options[:clusters] = s - end - opts.separator '' - opts.separator 'Common options:' - opts.on('-v', '--[no-]verbose', 'Run verbosely', 'Multiple -v options increase the verbosity. The maximum is 3.') do | | - options[:verbose] ||= 0 - options[:verbose] = options[:verbose] + 1 - end - - # Print an options summary. - opts.on_tail('-h', '--help', 'Show this message') do - puts opts - exit - end - end.parse! - - puts "Options: #{options}" if options[:verbose] - - return options -end - -ret = true -options = parse_command_line_parameters - -options[:sites].each do |site| - puts "Checking site #{site}..." - resources = JSON::parse(open("https://api-proxy.nancy.grid5000.fr/3.0/sites/#{site}/internal/oarapi/resources/details.json?limit=1000000", {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).read)['items'] - - default_resources = resources.select { |e| e['type'] == 'default' }.sort_by { |e| e['id'] } - - # Checking scheduler_priority - default_resources.each do |r| - if r['scheduler_priority'] < 0 - puts "Invalid scheduler_priority value on #{r['id']}/#{r['network_address']}: #{r['scheduler_priority']}" - ret = false - end - end - - # Checking list of properties - names = default_resources.map { |e| e.keys.sort }.uniq.first - IGNORED_PROPERTIES - if names != G5K_PROPERTIES - puts "ERROR: wrong list of properties:" - ret = false - puts "- " + (G5K_PROPERTIES - names).join(' ') - puts "+ " + (names - G5K_PROPERTIES).join(' ') - end - - # 'core' must be globally unique - dupe_cores = default_resources.map { |e| e.slice('id', 'core', 'host', 'cpu', 'cpuset') }.group_by { |e| e['core'] }.to_a.select { |e| e[1].length > 1 } - unless dupe_cores.empty? - puts "ERROR: some resources have the same 'core' value. it should be globally unique over the site." - ret = false - pp dupe_cores if options[:verbose] - end - - # 'cpu' must be unique to a 'host' - dupe_cpus = default_resources.map { |e| [e['cpu'], e['host'] ]}.uniq.group_by { |e| e[0] }.to_a.select { |e| e[1].length > 1 } - unless dupe_cpus.empty? - puts "ERROR: some hosts have the same 'cpu' value. it should be globally unique over the site." - ret = false - pp dupe_cores if options[:verbose] - end - - # for each host ... - default_resources.map { |e| e['host'] }.uniq.each do |host| - host_resources = default_resources.select { |e| e['host'] == host } - cluster = host_resources.first['cluster'] - next if not options[:clusters].empty? and not options[:clusters].include?(cluster) - - # compute nbcores. - # cpucore is cores per cpu. to know the number of cpus, we devide memnode per memcpu. - nbcores = host_resources.map { |e| e['cpucore'] * (e['memnode'] / e['memcpu']) }.uniq - if nbcores.length > 1 - raise "Invalid: varying nbcores inside cluster!" - end - nbcores = nbcores.first - - if host_resources.length != nbcores - puts "ERROR: invalid number of resources for #{host}. should be nbcores." - ret = false - end - - # ids and cores should be in the same order - host_cores = host_resources.map { |e| e['core'] } - host_cores_min = host_cores.first - host_cores_max = host_cores.last - if host_cores_max - host_cores_min + 1 != nbcores - puts "ERROR: core values for #{host} are not sequential" - ret = false - end - # the first cpuset should be 0 - host_cpusets = host_resources.map { |e| e['cpuset'] }.sort - host_cpusets_min = host_cpusets.first - host_cpusets_max = host_cpusets.last - if host_cpusets_min != 0 - puts "ERROR: first cpuset value for #{host} should be 0" - ret = false - end - # the last cpuset should be nbcores-1 - if host_cpusets_max - host_cpusets_min + 1 != nbcores - puts "ERROR: cpuset values for #{host} are not sequential" - ret = false - end - if options[:verbose] and (host_cpusets_max - host_cpusets_min + 1 != nbcores or host_cores_max - host_cores_min + 1 != nbcores) - puts "id cpu core cpuset" - pp host_resources.map { |e| [e['id'], e['cpu'], e['core'], e['cpuset'] ] } +module RefRepo::Valid::OarProperties + def self.check(options) + ret = true + options[:sites].each do |site| + puts "Checking site #{site}..." + resources = RefRepo::Utils::get_api("sites/#{site}/internal/oarapi/resources/details.json?limit=1000000")['items'] + + default_resources = resources.select { |e| e['type'] == 'default' }.sort_by { |e| e['id'] } + + # Checking scheduler_priority + default_resources.each do |r| + if r['scheduler_priority'] < 0 + puts "Invalid scheduler_priority value on #{r['id']}/#{r['network_address']}: #{r['scheduler_priority']}" + ret = false + end + end + + # Checking list of properties + names = default_resources.map { |e| e.keys.sort }.uniq.first - IGNORED_PROPERTIES + if names != G5K_PROPERTIES + puts "ERROR: wrong list of properties:" + ret = false + puts "- " + (G5K_PROPERTIES - names).join(' ') + puts "+ " + (names - G5K_PROPERTIES).join(' ') + end + + # 'core' must be globally unique + dupe_cores = default_resources.map { |e| e.slice('id', 'core', 'host', 'cpu', 'cpuset') }.group_by { |e| e['core'] }.to_a.select { |e| e[1].length > 1 } + unless dupe_cores.empty? + puts "ERROR: some resources have the same 'core' value. it should be globally unique over the site." + ret = false + pp dupe_cores if options[:verbose] + end + + # 'cpu' must be unique to a 'host' + dupe_cpus = default_resources.map { |e| [e['cpu'], e['host'] ]}.uniq.group_by { |e| e[0] }.to_a.select { |e| e[1].length > 1 } + unless dupe_cpus.empty? + puts "ERROR: some hosts have the same 'cpu' value. it should be globally unique over the site." + ret = false + pp dupe_cores if options[:verbose] + end + + # for each host ... + default_resources.map { |e| e['host'] }.uniq.each do |host| + host_resources = default_resources.select { |e| e['host'] == host } + cluster = host_resources.first['cluster'] + next if not options[:clusters].empty? and not options[:clusters].include?(cluster) + + # compute nbcores. + # cpucore is cores per cpu. to know the number of cpus, we devide memnode per memcpu. + nbcores = host_resources.map { |e| e['cpucore'] * (e['memnode'] / e['memcpu']) }.uniq + if nbcores.length > 1 + raise "Invalid: varying nbcores inside cluster!" + end + nbcores = nbcores.first + + if host_resources.length != nbcores + puts "ERROR: invalid number of resources for #{host}. should be nbcores." + ret = false + end + + # ids and cores should be in the same order + host_cores = host_resources.map { |e| e['core'] } + host_cores_min = host_cores.first + host_cores_max = host_cores.last + if host_cores_max - host_cores_min + 1 != nbcores + puts "ERROR: core values for #{host} are not sequential" + ret = false + end + # the first cpuset should be 0 + host_cpusets = host_resources.map { |e| e['cpuset'] }.sort + host_cpusets_min = host_cpusets.first + host_cpusets_max = host_cpusets.last + if host_cpusets_min != 0 + puts "ERROR: first cpuset value for #{host} should be 0" + ret = false + end + # the last cpuset should be nbcores-1 + if host_cpusets_max - host_cpusets_min + 1 != nbcores + puts "ERROR: cpuset values for #{host} are not sequential" + ret = false + end + if options[:verbose] and (host_cpusets_max - host_cpusets_min + 1 != nbcores or host_cores_max - host_cores_min + 1 != nbcores) + puts "id cpu core cpuset" + pp host_resources.map { |e| [e['id'], e['cpu'], e['core'], e['cpuset'] ] } + end + end end + return ret end end - -exit ret