Commit 6be2694f authored by Samir Noir's avatar Samir Noir 🧀
Browse files

Return a global view of reference-repository

parent 5e425148
Pipeline #163649 passed with stages
in 20 minutes
require 'resources_controller'
class GlobalController < ResourcesController
def index
allow :get
fetch('/')
end
def show_site
allow :get
fetch("/sites/#{params[:site_id]}")
end
def show_job
allow :get
fetch("/sites/#{params[:site_id]}")
end
protected
def collection_path
global_path
end
def links_for_collection
[
{
"rel" => "self",
"href" => uri_to(collection_path),
"type" => api_media_type(:g5kcollectionjson)
},
{
"rel" => "parent",
"href" => uri_to(parent_path),
"type" => api_media_type(:g5kitemjson)
}
]
end
end
......@@ -36,13 +36,18 @@ class ResourcesController < ApplicationController
raise NotFound, "Cannot find resource #{path}" if object.nil?
if object.has_key?('items')
# To avoid code duplication with global_controller
if params[:controller] == 'global'
object['links'] = links_for_collection
object['items'].each do |item|
item['links'] = links_for_item(item)
end
else
object['links'] = links_for_item(object)
if object.has_key?('items')
object['links'] = links_for_collection
object['items'].each do |item|
item['links'] = links_for_item(item)
end
else
object['links'] = links_for_item(object)
end
end
object['version'] = repository.commit.oid
......@@ -90,13 +95,22 @@ class ResourcesController < ApplicationController
params[:queues].split(',')
end
end
if params[:controller] == 'global'
params[:global] = true
if params[:action] == 'show_job'
params[:version] = OAR::Job.expanded.find(params[:job_id]).start_time
end
end
end
def lookup_path(path, params)
object = repository.find(
path.gsub(%r{/?platforms}, ''),
branch: params[:branch],
version: params[:version]
version: params[:version],
global: params[:global]
)
raise ServerUnavailable if object.is_a?(Exception)
......@@ -123,6 +137,26 @@ class ResourcesController < ApplicationController
# Finally, set new 'total' to clusters shortlisted
object['total'] = object['items'].length
when %w[global show_job]
assigned_nodes = OAR::Job.expanded.find(
params[:job_id]
).assigned_nodes
clusters = {}
assigned_nodes.each do |n|
clusters[n.gsub(/([a-z]+)-[0-9]+.*/, '\1')] ||= []
clusters[n.gsub(/([a-z]+)-[0-9]+.*/, '\1')] << n.gsub(/([a-z]+-[0-9]+).*/, '\1')
end
object['items'].delete_if { |key| !%w[clusters type uid].include?(key) }
object['items']['clusters'].delete_if { |key, _| !clusters.keys.include?(key) }
clusters.each do |cluster, nodes|
if object['items']['clusters'][cluster]
object['items']['clusters'][cluster]['nodes'].delete_if { |key, _| !nodes.include?(key) }
end
end
object['total'] = object['items'].length
end
object
......
......@@ -23,6 +23,9 @@ Api::Application.routes.draw do
get '/versions/:id' => 'versions#show', :via => [:get]
get '*resource/versions' => 'versions#index', :via => [:get]
get '*resource/versions/:id' => 'versions#show', :via => [:get]
get '/global' => 'global#index', :via => [:get]
get '/global/sites/:site_id' => 'global#show_site', :via => [:get]
get '/global/sites/:site_id/jobs/:job_id' => 'global#show_job', :via => [:get]
resources :environments, only: %i[index show], constraints: { id: /[0-9A-Za-z\-\.]+/ }
resources :network_equipments, only: %i[index show]
......
......@@ -15,6 +15,7 @@
require 'json'
require 'logger'
require 'rugged'
require 'hash'
module Grid5000
class Repository
......@@ -52,7 +53,12 @@ module Grid5000
return e
end
result = expand_object(object, path, @commit)
result = if options[:global]
global_expand(object, path, @commit)
else
expand_object(object, path, @commit)
end
result
end
......@@ -110,6 +116,53 @@ module Grid5000
end
end
def global_expand(hash_object, path, commit)
return nil if hash_object.nil?
tree_object = instance.lookup(hash_object[:oid])
# If it's a symlink
if hash_object[:filemode] == 40960
hash_object = find_object_at(instance.lookup(entry[:oid]).content, commit, File.join(path, root, entry[:name]))
tree_object = instance.lookup(hash_object[:oid])
end
global_hash = {}
tree_object.walk_blobs(:postorder) do |root, entry|
next unless File.extname(entry[:name]) == '.json'
# If it's a symlink
if entry[:filemode] == 40960
hash_object = find_object_at(instance.lookup(entry[:oid]).content, commit, File.join(path, root, entry[:name]))
object = instance.lookup(hash_object[:oid])
else
object = instance.lookup(entry[:oid])
end
path_hierarchy = File.dirname("#{root}#{entry[:name]}").split('/')
file_hash = JSON.parse(object.content)
path_hierarchy = [] if path_hierarchy == ['.']
if ['nodes', 'network_equipments', 'servers', 'pdus'].include?(path_hierarchy.last)
# it's a node or a network_equipment, add the uid
path_hierarchy << file_hash['uid']
end
file_hash = Hash.from_array(path_hierarchy, file_hash)
global_hash = global_hash.deep_merge(file_hash)
global_hash
end
result = {
"total" => global_hash.length,
"offset" => 0,
"items" => rec_sort(global_hash),
"version" => commit.oid
}
result
end
def find_commit_for(options = {})
options[:branch] ||= 'master'
version, branch = options.values_at(:version, :branch)
......
# coding: utf-8
# Monkey patching Ruby's Hash class
# Extend Hash with helper methods needed to convert input data files to ruby Hash
class Hash
# Recursively merge this Hash with another (ie. merge nested hash)
# Returns a new hash containing the contents of other_hash and the contents of hash. The value for entries with duplicate keys will be that of other_hash:
# a = {"key": "value_a"}
# b = {"key": "value_b"}
# a.deep_merge(b) -> {:key=>"value_b"}
# b.deep_merge(a) -> {:key=>"value_a"}
def deep_merge(other_hash)
merger = proc { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
self.merge(other_hash, &merger)
end
# Add an element composed of nested Hashes made from elements found in "array" argument
# i.e.: from_array([a, b, c],"foo") -> {a: {b: {c: "foo"}}}
def self.from_array(array, value)
return array.reverse.inject(value) { |a, n| { n => a } }
end
end
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
require 'spec_helper'
describe GlobalController do
render_views
describe "GET /global" do
it "should get the correct collection of items" do
get :index, :format => :json
expect(response.status).to eq 200
expect(json['total']).to eq 4
expect(json['items'].length).to eq 4
expect(json['items']['sites']).to be_a(Hash)
expect(json['items']['environments']).to be_a(Hash)
end
it "should get the correct collection of sites" do
get :index, :format => :json
expect(response.status).to eq 200
expect(json['items']['sites'].length).to eq 4
expect(json['items']['sites']['bordeaux'].length).to eq 14
expect(json['items']['sites']['bordeaux']).to be_a(Hash)
expect(json['items']['sites']['bordeaux']['uid']).to eq 'bordeaux'
end
it "should be the correct version" do
get :index, :format => :json
expect(response.status).to eq 200
expect(json['version']).to eq '8a562420c9a659256eeaafcfd89dfa917b5fb4d0'
end
end
describe "GET /global/sites/{{site_id}}" do
it "should get the correct collection for one site" do
get :show_site, params: { :site_id => 'rennes', :format => :json }
expect(response.status).to eq 200
expect(json['total']).to eq 14
expect(json['items'].length).to eq 14
expect(json['items']['clusters']).to be_a(Hash)
expect(json['items']['clusters']['paravent']['uid']).to eq 'paravent'
end
end
describe "GET /global/sites/{{site_id}}/jobs/{{job_id}}" do
it "should get the correct nodes collection for a job" do
get :show_job, params: { :site_id => 'rennes', :job_id => '374191', :format => :json }
expect(response.status).to eq 200
expect(json['total']).to eq 3
expect(json['items'].length).to eq 3
expect(json['items']['clusters']).to be_a(Hash)
expect(json['items']['clusters']['paramount']['uid']).to eq 'paramount'
expect(json['items']['clusters']['paramount']['nodes'].length).to eq 4
expect(json['version']).to eq '5b02702daa827f7e39ebf7396af26735c9d2aacd'
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