Commit 1ae4ad74 authored by MARGERY David's avatar MARGERY David
Browse files

make status code readable again

reorganize status code to separate cleanly the different steps of the
algorithm, and to handle all data structures specific to nodes in a
generic way.
parent f2723bd4
......@@ -35,7 +35,54 @@ module OAR
value
end
def api_type
Resource.api_type(type)
end
def api_name
case type
when 'default'
network_address
when 'disk'
[disk.split('.').first, host].join('.')
else
resource_id
end
end
class << self
def api_type(oar_type)
if oar_type=="default"
"nodes"
else
oar_type.pluralize
end
end
def list_some(options)
# Do OAR resources have a comment column
include_comment = columns.find{|c| c.name == "comment"}
# abasu for bug 5106 : we need cluster & core
# dmargery for bug 9230 : we need type, disk and diskpath
resources = Resource.select(
"resource_id, type, cluster, host, network_address, disk, diskpath, core, state, available_upto#{include_comment ? ", comment" : ""}"
)
resources = resources.where(
'"network_address" = ? OR "host" = ?',options[:network_address],options[:network_address]
) unless options[:network_address].blank?
resources = resources.where(
:cluster => options[:clusters]
) unless options[:clusters].blank?
resources = resources.where(:type => options[:oar_types])
return resources
end
# Returns the status of all resources from the types requested,
# indexed_by type
# in addition to OAR's types, node is supported, where
......@@ -65,108 +112,95 @@ module OAR
# }
#
def status(options = {})
result = {}
# Handle options
# No types requested implies default
# and default is returned as nodes
options[:types]=["node"] if options[:types].nil?
options[:types].each do |t| result[type_key(t)]={} end
job_details = options[:job_details] != 'no'
include_comment = columns.find{|c| c.name == "comment"}
# abasu for bug 5106 : added cluster & core in MySQL request - 05.02.2015
resources = Resource.select(
"resource_id, type, cluster, host, network_address, disk, diskpath, core, state, available_upto#{include_comment ? ", comment" : ""}"
)
options[:oar_types]=options[:types]
had_node=options[:oar_types].delete("node")=="node"
options[:oar_types].push("default") if had_node
resources = resources.where(
'"network_address" = ? OR "host" = ?',options[:network_address],options[:network_address]
) unless options[:network_address].blank?
resources = resources.where(
:cluster => options[:clusters]
) unless options[:clusters].blank?
# Control verbosity of result
# job_details controls whether future reservations
# of a given resources are returned
include_details = options[:job_details] != 'no'
# only look for requested types
# handle node as an alias for default
had_node=options[:types].delete("node")=="node"
options[:types].push("default") if had_node
resources = resources.where(:type => options[:types])
# Build the list of resources for which status is requested
resource_list=list_some(options)
resources = resources.index_by(&:resource_id)
# Build the list of active jobs with the resources they use
active_jobs=get_active_jobs_with_resources(options).values
# abasu : Introduce a hash table to store counts of free / busy cores per node - 05.02.2015
# abasu : This hash table can be used to store other counters in future (add another element)
nodes_counter = {}
resources.each do |resource_id, resource|
api_status = {}
api_status_data = {} # used later to aggregate oar resource status date at api resource level
# answer with some data for all requested types
# even when no resources of that type can be found
options[:oar_types].each do |oar_type|
api_status[api_type(oar_type)]={}
api_status_data[api_type(oar_type)]={}
end
resources={}
# Go though the list of resource (oar's definition) to
# - index by resource_id (.index_by(&:resource_id))
# - set the API status of resources (API's definition) with no jobs ;
# - setup any data required to compute the API status when it depends on the
# status of multiple OAR resources (eg. nodes)
resource_list.each do |resource|
next if resource.nil?
resources[resource.resource_id]=resource
result[type_key(resource.type)][get_status_key(resource)] ||= initial_status_for(resource, job_details)
api_status[resource.api_type][resource.api_name] ||= initial_status_for(resource, include_details)
if resource.type=='default'
nodes_counter[resource.network_address]= {
:totalcores => 0,
:busycounter => 0,
:besteffortcounter => 0
} if !nodes_counter.has_key?(resource.network_address)
nodes_counter[resource.network_address][:totalcores] += 1
end
end # .each do |resource_id, resource|
api_status_data[resource.api_type][resource.api_name] ||= initial_status_data_for(resource, include_details)
get_active_jobs_by_moldable_id(options).each do |moldable_id, h|
current = h[:job].running?
api_status_data[resource.api_type][resource.api_name]=
update_with_resource(api_status_data[resource.api_type][resource.api_name],
resource,
include_details)
end # .each do |resource_id, resource|
# prepare job description now, since it will be added to each resource
# For Result hash table, do not include events
# Go through active jobs and update status data for all the
# resources of the job
active_jobs.each do |h|
# prepare job description now, since it will be the same for each
# resource of the job
# For api_status hash table, do not include events
# (otherwise the Set does not work with nested hash)
jobh = h[:job].to_reservation(:without => :events) if job_details
job_for_api = nil
job_for_api = h[:job].to_reservation(:without => :events) if include_details
h[:resources].each do |resource_id|
resource = resources[resource_id]
# The resource does not belong to a cluster the caller is interested in.
# The resource does not belong to the list of resources the caller is interested in.
next if resource.nil?
# abasu : if job is current, increment corresponding counter(s) in hash table
if current
if resource.type=='default'
nodes_counter[resource.network_address][:busycounter] += 1
if h[:job].besteffort?
nodes_counter[resource.network_address][:besteffortcounter] += 1
end # if h[:job].besteffort?
else
result[type_key(resource.type)][get_status_key(resource)][:soft] = 'busy'
end
end # if current
result[type_key(resource.type)][get_status_key(resource)][:reservations].add(jobh) if job_details
api_status_data[resource.api_type][resource.api_name]=
update_with_job(resource.api_type,
api_status_data[resource.api_type][resource.api_name],
h[:job],
job_for_api)
end # .each do |resource_id|
end # .each do |moldable_id, h|
# abasu : At this stage we have the the complete status over all cores in each node (network_address)
# abasu : Now add logic to sum up the status over all cores and push final status to result hash table
nodes_counter.each do |network_address, node_counter|
next if result["nodes"][network_address].nil?
next if result["nodes"][network_address][:hard] == 'dead'
if node_counter[:busycounter] == 0
result["nodes"][network_address][:soft] = "free" # all cores in node are free
end
if node_counter[:busycounter] > 0 && node_counter[:busycounter] <= node_counter[:totalcores] / 2
result["nodes"][network_address][:soft] = "free_busy" # more free cores in node than busy cores
end # .each do |h|
# We now compute the final status from the api_status_data
api_status_data.each do |api_type, type_status_data|
type_status_data.each do |api_resource_name, aggregatated_status_data|
api_status[api_type] ||= {}
api_status[api_type][api_resource_name]=derive_api_status(api_type,
api_status[api_type][api_resource_name],
aggregatated_status_data)
end
if node_counter[:busycounter] > node_counter[:totalcores] / 2 && node_counter[:busycounter] < node_counter[:totalcores]
result["nodes"][network_address][:soft] = "busy_free" # more busy cores in node than free cores
end
if node_counter[:busycounter] == node_counter[:totalcores]
result["nodes"][network_address][:soft] = "busy" # all cores in node are busy
end # nested if
if node_counter[:besteffortcounter] > 0
result["nodes"][network_address][:soft] += "_besteffort" # add "_besteffort" after status if it is so
end # if node_counter[:besteffortcounter]
end # .each do |network_address, node_counter|
end
result
api_status
end # def status
def get_active_jobs_by_moldable_id(options = {})
def get_active_jobs_with_resources(options = {})
active_jobs_by_moldable_id = {}
jobs = options[:waiting] == 'no' ? Job.expanded.active_not_waiting : Job.expanded.active
jobs.find(:all, :include => [:job_types]).
......@@ -209,50 +243,93 @@ module OAR
active_jobs_by_moldable_id
end
def type_key(type)
if type=="default"
"nodes"
else
type.pluralize
end
end
def get_status_key(resource)
case resource.type
when 'default'
resource.network_address
when 'disk'
[resource.disk.split('.').first, resource.host].join('.')
else
resource.id
end
end
# Returns the initial status hash for a resource.
def initial_status_for(resource, job_details)
hard = resource.state
# Returns the status hash for a resource with no jobs
def initial_status_for(resource, include_details)
h = {:hard => resource.state}
# Check if resource is in standby state
if hard == 'absent' && resource.available_upto && resource.available_upto == STANDBY_AVAILABLE_UPTO
hard = 'standby'
if resource.state == 'absent' && resource.available_upto && resource.available_upto == STANDBY_AVAILABLE_UPTO
h[:hard] = 'standby'
end
h = {:hard => hard}
case resource.type
when 'default'
h[:soft]= resource.dead? ? "unknown" : "free"
h[:comment] = resource.comment if resource.respond_to?(:comment)
h
when 'disk'
h = {
:soft => "free",
:diskpath => resource.diskpath,
}
h[:soft]= "free"
h[:diskpath] = resource.diskpath
end
h[:reservations] = Set.new if job_details
h
end # def initial_status_for
# Creates accumulator for resources described at API level
# that are an aggregation of OAR resources
# so as to be able to compute their aggregated status
def initial_status_data_for(resource, include_details)
initial_data=
if resource.api_type=="nodes"
{
:totalcores => 0,
:busycounter => 0,
:besteffortcounter => 0
}
else
{}
end
initial_data[:reservations]=Set.new if include_details
initial_data
end
def update_with_resource(current_data, resource, include_details)
current_data[:totalcores] += 1 if resource.api_type=="nodes"
return current_data
end
def update_with_job(api_type, current_data, oar_job, job_for_api)
if oar_job.running?
if api_type=="nodes"
current_data[:busycounter] += 1
if oar_job.besteffort?
current_data[:besteffortcounter] += 1
end
else
current_data[:soft] = 'busy'
end
end
current_data[:reservations].add(job_for_api) unless job_for_api.nil?
return current_data
end
def derive_api_status(api_type, initial_status, current_data)
derived_status=initial_status
[:reservations, :soft].each do |key|
if current_data.has_key?(key)
derived_status[key]=current_data[key]
end
end
#do specific calculation for some api_types
if api_type=="nodes"
# abasu : At this stage we have the the complete status over all cores in each node (network_address)
# abasu : Now add logic to sum up the status over all cores and push final status to api_status hash table
if current_data[:busycounter] > 0
if current_data[:busycounter] <= current_data[:totalcores] / 2
derived_status[:soft] = "free_busy" # more free cores in node than busy cores
elsif current_data[:busycounter] > current_data[:totalcores] / 2 && current_data[:busycounter] < current_data[:totalcores]
derived_status[:soft] = "busy_free" # more busy cores in node than free cores
else
derived_status[:soft] = "busy" # all cores in node are busy
end
if current_data[:besteffortcounter] > 0
derived_status[:soft] += "_besteffort" # add "_besteffort" after status if it is so
end
derived_status[:freeslots]=current_data[:totalcores]-current_data[:busycounter]
derived_status[:freeableslots]=current_data[:besteffortcounter]
end
end
derived_status
end
end # class << self
end # class Resource
end # module OAR
Supports Markdown
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