Commit 5f994c4e authored by Jérémie Gaidamour's avatar Jérémie Gaidamour
Browse files

[dev] A first version of the new generator for oar properties

parent 7370a8dc
#!/usr/bin/ruby
require 'pp'
require 'erb'
require 'fileutils'
require 'pathname'
require 'json'
require 'time'
require 'yaml'
require 'hashdiff'
class MissingProperty < StandardError; end
MiB = 1024**2
# Get node properties from the reference repo hash
def get_node_properties(cluster_uid, cluster, node_uid, node)
h = {} # ouput
main_network_adapter = node['network_interfaces'].values.find{ |na| na['enabled'] && na['mounted'] && na['interface'] =~ /ethernet/i && !na['management'] }
main_network_adapter = node['network_interfaces'].values.find{ |na| na['enabled'] && na['mounted'] }
raise MissingProperty, "Node #{node_uid} does not have a main network_adapter" unless main_network_adapter
# h['host'] = main_network_adapter['network_address']
#TODO raise MissingProperty, "Node #{node_uid} has no network_address" unless h['host']
h['ip'] = main_network_adapter['ip']
raise MissingProperty, "Node #{node_uid} has no IP" unless h['ip']
h['cluster'] = cluster_uid
h['nodemodel'] = cluster['model']
h['switch'] = main_network_adapter['switch']
h['besteffort'] = node['supported_job_types']['besteffort']
h['deploy'] = node['supported_job_types']['deploy']
h['ip_virtual'] = node['supported_job_types']['virtual'] == 'ivt'
h['virtual'] = node['supported_job_types']['virtual']
h['cpuarch'] = node['architecture']['platform_type']
h['cpucore'] = node['architecture']['smt_size']/node['architecture']['smp_size']
h['cputype'] = [node['processor']['model'], node['processor']['version']].join(' ')
h['cpufreq'] = node['processor']['clock_speed']/1_000_000_000.0
h['disktype'] = (node['block_devices'].first[1] || {})['interface']
h['ethnb'] = node['network_interfaces'].values.select{|na| na['interface'] =~ /ethernet/i}.select{|nb| nb['mountable'] == true}.length
eth10g = node['network_interfaces'].values.select{|na| na['interface'] =~ /ethernet/i}.select{|nb| nb['mountable'] == true}
h['eth10g'] = eth10g.detect{|na| na['rate'] == 10_000_000_000}.nil? ? false : true
ib10g = node['network_interfaces'].values.detect{|na| na['interface'] =~ /infiniband/i && na['rate'] == 10_000_000_000}
h['ib10g'] = ib10g ? true : false
h['ib10gmodel'] = ib10g ? ib10g['version'] : 'none'
ib20g = node['network_interfaces'].values.detect{|na| na['interface'] =~ /infiniband/i && na['rate'] == 20_000_000_000}
h['ib20g'] = ib20g ? true : false
h['ib20gmodel'] = ib20g ? ib20g['version'] : 'none'
ib40g = node['network_interfaces'].values.detect{|na| na['interface'] =~ /infiniband/i && na['rate'] == 40_000_000_000}
h['ib40g'] = ib40g ? true : false
h['ib40gmodel'] = ib40g ? ib40g['version'] : 'none'
ib56g = node['network_interfaces'].values.detect{|na| na['interface'] =~ /infiniband/i && na['rate'] == 56_000_000_000}
h['ib56g'] = ib56g ? true : false
h['ib56gmodel'] = ib56g ? ib56g['version'] : 'none'
myri10g = node['network_interfaces'].values.detect{|na| na['interface'] =~ /myri/i && na['rate'] == 10_000_000_000}
h['myri10g'] = myri10g ? true : false
h['myri10gmodel'] = myri10g ? myri10g['version'] : 'none'
myri2g = node['network_interfaces'].values.detect{|na| na['interface'] =~ /myri/i && na['rate'] == 2_000_000_000}
h['myri2g'] = myri2g ? true : false
h['myri2gmodel'] = myri2g ? myri2g['version'] : 'none'
h['memcore'] = node['main_memory']['ram_size']/node['architecture']['smt_size']/MiB
h['memcpu'] = node['main_memory']['ram_size']/node['architecture']['smp_size']/MiB
h['memnode'] = node['main_memory']['ram_size']/MiB
node['gpu'] ||= {}
h['gpu'] = case node['gpu']['gpu'] when true; true; when false; false when nil; false; else node['gpu']['gpu'].upcase end
h['gpu_count'] = node['gpu']['gpu_count']
h['gpu_model'] = node['gpu']['gpu_model']
node['monitoring'] ||= {}
h['wattmeter'] = case node['monitoring']['wattmeter'] when true; 'YES'; when false; 'NO' when nil; 'NO'; else node['monitoring']['wattmeter'].upcase end
# h['rconsole'] = node['monitoring']['rconsole']
h['cluster_priority'] = (cluster['priority'] || Time.parse(cluster['created_at'].to_s).strftime('%Y%m')).to_i
begin
h['production'] = node['supported_job_types']['queues'].include?('production') ? true : false
rescue
# Set as NO by default
h['production'] = 'NO'
end
h['max_walltime'] = node['supported_job_types']['max_walltime'] || 0
h
end
#
#
#
def get_nodelist_properties(site_uid, site)
properties = {} # output
site['clusters'].each do |cluster_uid, cluster|
cluster['nodes'].each do |node_uid, node|
begin
properties[node_uid] = get_node_properties(cluster_uid, cluster, node_uid, node)
rescue MissingProperty => e
# puts "Error while processing node #{node_uid}: #{e}"
end
end
end
return properties
end
def diff_node_properties(a, b)
ignore_keys = [
"slash_16",
"slash_17",
"slash_18",
"slash_19",
"slash_20",
"slash_21",
"slash_22",
"available_upto",
"chunks",
"comment", # TODO
"core",
"cpu",
"cpuset",
"desktop_computing",
"drain",
"expiry_date",
"finaud_decision",
"grub",
"host", # TODO
"last_available_upto",
"last_job_date",
"maintenance",
"mic", # TODO
"network_address", # TODO
"next_finaud_decision",
"next_state",
"rconsole", # TODO
"resource_id",
"scheduler_priority",
"state",
"state_num",
"subnet_address",
"subnet_prefix",
"suspended_jobs",
"thread",
"type", # TODO
"vlan",
"ib56g", # TODO
"ib56gmodel", # TODO
"wattmeter" # TODO
]
ignore_keys.each { |key| a.delete(key) }
ignore_keys.each { |key| b.delete(key) }
return HashDiff.diff(a, b)
end
#def cmd_set_oarnodesetting(properties)
# properties.each
#end
def oarcmd_set_node_properties(host, properties)
return "# #{host}: OK" if properties.size == 0
command = "# #{host}:\n"
command += "#{ENV["SUDO"]} oarnodesetting -h #{host} -p "
command +=
properties.to_a.map{ |(k,v)|
v = "YES" if v == true
v = "NO" if v == false
! v.nil? ? "#{k}=#{v.inspect.gsub("'", "\\'").gsub("\"", "'")}" : nil
}.compact.join(' -p ')
return command
end
def oarcmd_get_nodelist_properties()
# Get the current OAR properties from the OAR scheduler
h = YAML.load_file('../../JG/nancy-oarnodes.yaml')
h = h.map {|k, v| v['type'] == 'default' ? [v['host'].split('.').first, v] : [nil, nil] }.to_h
return h
end
#!/usr/bin/ruby
require 'pp'
require 'erb'
require 'fileutils'
require 'pathname'
require 'json'
require 'time'
require '../lib/input_loader'
# Output directory
refapi_path = "/tmp/data"
#global_hash = JSON.parse(STDIN.read)
#global_hash = load_yaml_file_hierarchy("../../input/example/")
global_hash = load_yaml_file_hierarchy("../../input/grid5000/")
class Hash
# sort a hash according to the position of the key in the array
def sort_by_array(array)
Hash[sort_by{|key, _| array.index(key) || length}]
end
end
# Write pretty and sorted JSON files
def write_json(filepath, data)
def rec_sort(h)
case h
when Array
h.map{|v| rec_sort(v)}#.sort_by!{|v| (v.to_s rescue nil) }
when Hash
Hash[Hash[h.map{|k,v| [rec_sort(k),rec_sort(v)]}].sort_by{|k,v| [(k.to_s rescue nil), (v.to_s rescue nil)]}]
else
h
end
end
File.open(filepath, 'w') do |f|
f.write(JSON.pretty_generate(rec_sort(data)))
end
end
# Parse network equipment description and return switch name and port connected to given node
# In the network description, if the node interface is given (using "port" attribute),
# the interface parameter must be used.
def net_switch_port_lookup(site, node_uid, interface='')
site["net-links"].each do |switch_uid, switch| # TODO: rename net-links<->network
#pp switch_uid
switch["linecards"].each do |lc_uid,lc|
lc["ports"].each do |port_uid,port|
if port.is_a?(Hash)
switch_remote_port = port["port"] || lc["port"] || ""
switch_remote_uid = port["uid"]
else
switch_remote_port = lc["port"] || ""
switch_remote_uid = port
end
if switch_remote_uid == node_uid and switch_remote_port == interface
# Build port name from snmp_naming_pattern
# Example: '3 2 GigabitEthernet%LINECARD%/%PORT%' -> 'GigabitEthernet3/2'
port_name = lc["snmp_pattern"].sub("%LINECARD%",lc_uid.to_s).sub("%PORT%",port_uid.to_s)
return switch_uid, port_name
end
end
end
end
return nil
end
global_hash["sites"].each do |site_uid, site|
pp site_uid
# pp site
site["clusters"].each do |cluster_uid, cluster|
pp cluster_uid
cluster_path = Pathname.new(refapi_path).join("sites",site_uid,"clusters",cluster_uid)
cluster_path.join("nodes").mkpath()
# Write cluster info w/o nodes entries
cluster["type"] = "cluster"
cluster["uid"] = cluster_uid
# On the previous version of this script, cluster["created_ad"] was generated from a Ruby Time. cluster["created_ad"] is now a Ruby Date at JSON import.
# As Date.httpdate and Time.httpdate does not behave the same with timezone, it is converted here as a Ruby time.
cluster["created_at"] = Time.parse(cluster["created_at"].to_s).httpdate
write_json(cluster_path.join("#{cluster_uid}.json"),
cluster.reject {|k, v| k == "nodes"})
# Write node info
cluster["nodes"].each do |node_uid, node|
pp node_uid
# next unless node_uid == "griffon-1"
node["uid"] = node_uid
node["type"] = "node"
if node.key?("processor")
node["processor"]["cache_l1"] = nil unless node["processor"].key?("cache_l1")
end
# Add default keys
node["gpu"] = {} unless node.key?("gpu")
node["gpu"]["gpu"] = false unless node["gpu"].key?("gpu")
node["main_memory"] = {} unless node.key?("main_memory")
node["main_memory"]["virtual_size"] = nil unless node["main_memory"].key?("virtual_size")
# node["monitoring"] = {} unless node.key?("monitoring")
# node["monitoring"]["wattmeter"] = false unless node["monitoring"].key?("wattmeter")
# Rename keys
node["storage_devices"] = node.delete("block_devices")
node["network_adapters"] = node.delete("network_interfaces")
if node.key?("chassis")
node["chassis"]["name"] = node["chassis"].delete("product_name")
node["chassis"]["serial"] = node["chassis"].delete("serial_number")
end
# Type conversion
node["network_adapters"].each { |key, hash| hash["rate"] = hash["rate"].to_i if hash["rate"].is_a?(Float) }
# Convert hashes to arrays
node["storage_devices"].each { |key, hash| node["storage_devices"][key]["device"] = key; } # Add "device: sdX" within the hash
node["storage_devices"] = node["storage_devices"].sort_by_array(["sda", "sdb", "sdc", "sdd", "sde"]).values
node["network_adapters"].each { |key, hash| node["network_adapters"][key]["device"] = key; } # Add "device: ethX" within the hash
node["network_adapters"] = node["network_adapters"].sort_by_array(["eth0", "eth1", "eth2", "eth3", "ib0", "ib1", "ib2", "ib3", "bmc"]).values
# Populate "network_address", "switch" and "switch_port" from the network equipment description for each network adapters
node["network_adapters"].each { |network_adapter|
# ib properties
network_adapter["ib_switch_card"] = network_adapter.delete("line_card") if network_adapter.key?("line_card")
network_adapter["ib_switch_card_pos"] = network_adapter.delete("position") if network_adapter.key?("position")
next unless network_adapter["enabled"]
# Management network_adapter (bmc)
if network_adapter["management"]
network_adapter["network_address"] = "#{node_uid}-bmc.#{site_uid}.grid5000.fr"
next
end
# if network_adapter["network_address"] or network_adapter["switch"] or network_adapter["switch_port"]
# pp "Warning: network_address, switch or switch_port defined manually for (#{node_uid}, #{network_adapter["device"]})"
# pp "#{network_adapter["network_address"]}, #{network_adapter["switch"]} or #{network_adapter["switch_port"]}"
# end
if network_adapter["mounted"] and /^eth[0-9]$/.match(network_adapter["device"])
# Primary network_adapter
network_adapter["network_address"] = "#{node_uid}.#{site_uid}.grid5000.fr"
# Interface may not be specified in Network Reference for primary network_adapter
network_adapter["switch"], network_adapter["switch_port"] = net_switch_port_lookup(site, node_uid, network_adapter["device"]) || net_switch_port_lookup(site, node_uid)
# network_adapter["bridged"] = true # TODO?
next
end
# if network_adapter["bridged"]
# Secondary network_adapter(s)
network_adapter["network_address"] = "#{node_uid}-#{network_adapter["device"]}.#{site_uid}.grid5000.fr"
switch, port = net_switch_port_lookup(site, node_uid, network_adapter["device"])
network_adapter["switch"] = switch if switch
network_adapter["switch_port"] = port if port
# end
}
if node.key?("sensors") and node.key?("pdu") and node["pdu"].key?("pdu_name")
node["sensors"]["power"] = {} unless node["sensors"].key?("power")
node["sensors"]["power"]["via"] = {} unless node["sensors"]["power"].key?("via")
node["sensors"]["power"]["via"]["pdu"] = [] unless node["sensors"]["power"]["via"].key?("pdu")
node["sensors"]["power"]["via"]["pdu"][0] = {} unless node["sensors"]["power"]["via"]["pdu"].size > 0
node["sensors"]["power"]["via"]["pdu"][0]["uid"] = node["pdu"]["pdu_name"]
node["sensors"]["power"]["via"]["pdu"][0]["port"] = node["pdu"]["pdu_position"] if node["pdu"].key?("pdu_position")
node.delete("pdu")
end
#pp cluster_path.join("nodes","#{node_uid}.json")
write_json(cluster_path.join("nodes","#{node_uid}.json"), node)
end
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment