ruby-cute 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +3 -1
- data/README.md +8 -7
- data/Rakefile +1 -0
- data/debian/changelog +2 -2
- data/examples/distem-bootstrap +113 -42
- data/examples/g5k-tutorial.md +999 -0
- data/lib/cute/extensions.rb +16 -0
- data/lib/cute/g5k_api.rb +82 -15
- data/lib/cute/synchronization.rb +1 -1
- data/lib/cute/version.rb +1 -1
- data/spec/g5k_api_spec.rb +5 -2
- data/spec/spec_helper.rb +6 -0
- metadata +4 -3
data/lib/cute/extensions.rb
CHANGED
@@ -36,3 +36,19 @@ class String
|
|
36
36
|
end
|
37
37
|
|
38
38
|
end
|
39
|
+
|
40
|
+
module Cute
|
41
|
+
|
42
|
+
class OARSSHopts < Hash
|
43
|
+
|
44
|
+
def initialize(opts={})
|
45
|
+
|
46
|
+
raise "The argument must be a Hash" unless opts.is_a?(Hash)
|
47
|
+
self.merge!({:user => "oar", :keys => ["~/my_ssh_jobkey"], :port => 6667 })
|
48
|
+
self.merge!(opts)
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/cute/g5k_api.rb
CHANGED
@@ -211,7 +211,9 @@ module Cute
|
|
211
211
|
|
212
212
|
machine =`uname -ov`.chop
|
213
213
|
@user_agent = "ruby-cute/#{VERSION} (#{machine}) Ruby #{RUBY_VERSION}"
|
214
|
-
@api = RestClient::Resource.new(@endpoint, :timeout => 30)
|
214
|
+
@api = RestClient::Resource.new(@endpoint, :timeout => 30,:verify_ssl => false)
|
215
|
+
# some versions of restclient do not verify by default SSL certificates , :verify_ssl => true)
|
216
|
+
# SSL verify is disabled due to Grid'5000 API certificate problem
|
215
217
|
@on_error = on_error
|
216
218
|
test_connection
|
217
219
|
end
|
@@ -280,6 +282,12 @@ module Cute
|
|
280
282
|
|
281
283
|
# Issues a Cute::G5K exception according to the http status code
|
282
284
|
def handle_exception(e)
|
285
|
+
|
286
|
+
unless e.respond_to? :http_code
|
287
|
+
raise e
|
288
|
+
end
|
289
|
+
|
290
|
+
# Handling G5k API errors
|
283
291
|
case e.http_code
|
284
292
|
when 500
|
285
293
|
# This part deals with bug: https://intranet.grid5000.fr/bugzilla/show_bug.cgi?id=5912
|
@@ -484,8 +492,8 @@ module Cute
|
|
484
492
|
# @option params [String] :conf_file Path for configuration file
|
485
493
|
# @option params [String] :uri REST API URI to contact
|
486
494
|
# @option params [String] :version Version of the REST API to use
|
487
|
-
# @option params [String] :
|
488
|
-
# @option params [String] :
|
495
|
+
# @option params [String] :username Username to access the REST API
|
496
|
+
# @option params [String] :password Password to access the REST API
|
489
497
|
# @option params [Symbol] :on_error Set to :ignore if you want to ignore {Cute::G5K::RequestFailed ResquestFailed} exceptions.
|
490
498
|
def initialize(params={})
|
491
499
|
config = {}
|
@@ -495,16 +503,20 @@ module Cute
|
|
495
503
|
params[:conf_file] = default_file if File.exist?(default_file)
|
496
504
|
end
|
497
505
|
|
506
|
+
params[:username] ||= params[:user]
|
507
|
+
params[:password] ||= params[:pass] # backward compatibility
|
508
|
+
|
498
509
|
config = YAML.load(File.open(params[:conf_file],'r')) unless params[:conf_file].nil?
|
499
|
-
@user = params[:
|
500
|
-
@pass = params[:
|
510
|
+
@user = params[:username] || config["username"]
|
511
|
+
@pass = params[:password] || config["password"]
|
501
512
|
@uri = params[:uri] || config["uri"]
|
502
513
|
@api_version = params[:version] || config["version"] || "sid"
|
503
514
|
@logger = nil
|
504
515
|
|
505
516
|
begin
|
506
517
|
@g5k_connection = G5KRest.new(@uri,@api_version,@user,@pass,params[:on_error])
|
507
|
-
rescue
|
518
|
+
rescue => e
|
519
|
+
|
508
520
|
msg_create_file = ""
|
509
521
|
if (not File.exist?(default_file)) && params[:conf_file].nil? then
|
510
522
|
msg_create_file = "Please create the file: ~/.grid5000_api.yml and
|
@@ -512,7 +524,9 @@ module Cute
|
|
512
524
|
:conf_file to indicate another file for the credentials"
|
513
525
|
end
|
514
526
|
raise "Unable to authorize against the Grid'5000 API.
|
515
|
-
|
527
|
+
#{e.message}
|
528
|
+
#{msg_create_file}"
|
529
|
+
|
516
530
|
|
517
531
|
end
|
518
532
|
end
|
@@ -656,7 +670,8 @@ module Cute
|
|
656
670
|
.map { |x| "#{x}.#{site}.grid5000.fr"}
|
657
671
|
switch['nodes'] = nodes
|
658
672
|
}
|
659
|
-
|
673
|
+
|
674
|
+
return items
|
660
675
|
end
|
661
676
|
|
662
677
|
# @return [Hash] information of a specific switch available in a given Grid'5000 site.
|
@@ -668,6 +683,37 @@ module Cute
|
|
668
683
|
return s
|
669
684
|
end
|
670
685
|
|
686
|
+
# Returns information using the Metrology API.
|
687
|
+
#
|
688
|
+
# = Example
|
689
|
+
#
|
690
|
+
# You can get detailed information of available metrics in a given site:
|
691
|
+
# get_metric("rennes")
|
692
|
+
#
|
693
|
+
# If you are only interested in the names of the available metrics:
|
694
|
+
# get_metric("rennes").uids #=> ["cpu_nice", "boottime", "bytes_in", ...]
|
695
|
+
#
|
696
|
+
# Then, you can get information about the probes available for a specific metric:
|
697
|
+
# get_metric("rennes",:metric => "network_in")
|
698
|
+
#
|
699
|
+
# Finally, you can query on a specific probe:
|
700
|
+
# get_metric("rennes",:metric => "network_in",:query => {:from => 1450374553, :to => 1450374553, :only => "parasilo-11-eth0"})
|
701
|
+
#
|
702
|
+
# @return [Array] information of a specific metric in a given Grid'5000 site.
|
703
|
+
# @param site [String] a valid Grid'5000 site name
|
704
|
+
# @param [Hash] opts Options for metric query
|
705
|
+
# @option opts [String] :metric specific metric to query on
|
706
|
+
# @option opts [Hash] :query timeseries parameters (e.g. only, resolution, from, to)
|
707
|
+
|
708
|
+
def get_metric(site,opts ={})
|
709
|
+
params = opts[:metric].nil? ? "" : "/#{opts[:metric]}/timeseries"
|
710
|
+
if opts[:query]
|
711
|
+
params+="?"
|
712
|
+
opts[:query].each{ |k,v| params+="#{k}=#{v}&"}
|
713
|
+
end
|
714
|
+
@g5k_connection.get_json(api_uri("sites/#{site}/metrics#{params}")).items
|
715
|
+
end
|
716
|
+
|
671
717
|
# Returns information of all my jobs submitted in a given site.
|
672
718
|
# By default it only shows the jobs in state *running*.
|
673
719
|
# You can specify another state like this:
|
@@ -835,6 +881,7 @@ module Cute
|
|
835
881
|
# @option opts [String] :name Reservation name
|
836
882
|
# @option opts [String] :cmd The command to execute when the job starts (e.g. ./my-script.sh).
|
837
883
|
# @option opts [String] :cluster Valid Grid'5000 cluster
|
884
|
+
# @option opts [String] :queue A specific job queue
|
838
885
|
# @option opts [Array] :subnets 1) prefix_size, 2) number of subnets
|
839
886
|
# @option opts [String] :env Environment name for {http://kadeploy3.gforge.inria.fr/ Kadeploy}
|
840
887
|
# @option opts [Symbol] :vlan Vlan type: :routed, :local, :global
|
@@ -847,7 +894,8 @@ module Cute
|
|
847
894
|
|
848
895
|
# checking valid options
|
849
896
|
valid_opts = [:site, :cluster, :switches, :cpus, :cores, :nodes, :walltime, :cmd,
|
850
|
-
:type, :name, :subnets, :env, :vlan, :properties, :resources,
|
897
|
+
:type, :name, :subnets, :env, :vlan, :properties, :resources,
|
898
|
+
:reservation, :wait, :keys, :queue, :env_user]
|
851
899
|
unre_opts = opts.keys - valid_opts
|
852
900
|
raise ArgumentError, "Unrecognized option #{unre_opts}" unless unre_opts.empty?
|
853
901
|
|
@@ -868,6 +916,7 @@ module Cute
|
|
868
916
|
resources = opts.fetch(:resources, "")
|
869
917
|
type = :deploy if opts[:env]
|
870
918
|
keys = opts[:keys]
|
919
|
+
queue = opts[:queue]
|
871
920
|
|
872
921
|
vlan_opts = {:routed => "kavlan",:global => "kavlan-global",:local => "kavlan-local"}
|
873
922
|
vlan = nil
|
@@ -881,11 +930,11 @@ module Cute
|
|
881
930
|
|
882
931
|
raise 'At least nodes, time and site must be given' if [nodes, walltime, site].any? { |x| x.nil? }
|
883
932
|
|
933
|
+
raise 'nodes should be an integer or a string containing either ALL or BEST' unless (nodes.is_a?(Fixnum) or ["ALL","BEST"].include?(nodes))
|
934
|
+
|
884
935
|
secs = walltime.to_secs
|
885
936
|
walltime = walltime.to_time
|
886
937
|
|
887
|
-
raise 'Nodes must be an integer.' unless nodes.is_a?(Integer)
|
888
|
-
|
889
938
|
command = "sleep #{secs}" if command.nil?
|
890
939
|
type = type.to_sym unless type.nil?
|
891
940
|
|
@@ -911,6 +960,7 @@ module Cute
|
|
911
960
|
|
912
961
|
payload['properties'] = properties unless properties.nil?
|
913
962
|
payload['types'] = [ type.to_s ] unless type.nil?
|
963
|
+
payload['queue'] = queue if queue
|
914
964
|
|
915
965
|
if not type == :deploy
|
916
966
|
if opts[:keys]
|
@@ -930,8 +980,7 @@ module Cute
|
|
930
980
|
# The request has to be redirected to the OAR API given that Grid'5000 API
|
931
981
|
# does not support some OAR options.
|
932
982
|
if payload['import-job-key-from-file'] then
|
933
|
-
|
934
|
-
payload["resources"] = "\"#{payload["resources"]}\""
|
983
|
+
|
935
984
|
temp = @g5k_connection.post_json(api_uri("sites/#{site}/internal/oarapi/jobs"),payload)
|
936
985
|
sleep 1 # This is for being sure that our job appears on the list
|
937
986
|
r = get_my_jobs(site,nil).select{ |j| j["uid"] == temp["id"] }.first
|
@@ -1035,7 +1084,8 @@ module Cute
|
|
1035
1084
|
|
1036
1085
|
# checking valid options, same as reserve option even though some option dont make any sense
|
1037
1086
|
valid_opts = [:site, :cluster, :switches, :cpus, :cores, :nodes, :walltime, :cmd,
|
1038
|
-
:type, :name, :subnets, :env, :vlan, :properties, :resources,
|
1087
|
+
:type, :name, :subnets, :env, :vlan, :properties, :resources,
|
1088
|
+
:reservation, :wait, :keys, :queue, :env_user]
|
1039
1089
|
|
1040
1090
|
unre_opts = opts.keys - valid_opts
|
1041
1091
|
raise ArgumentError, "Unrecognized option #{unre_opts}" unless unre_opts.empty?
|
@@ -1069,6 +1119,8 @@ module Cute
|
|
1069
1119
|
info "Found VLAN with uid = #{vlan}"
|
1070
1120
|
end
|
1071
1121
|
|
1122
|
+
payload['user'] = opts[:env_user] unless opts[:env_user].nil?
|
1123
|
+
|
1072
1124
|
info "Creating deployment"
|
1073
1125
|
|
1074
1126
|
begin
|
@@ -1135,9 +1187,10 @@ module Cute
|
|
1135
1187
|
#
|
1136
1188
|
# g5k = Cute::G5K::API.new()
|
1137
1189
|
#
|
1138
|
-
# job = g5k.reserve(:nodes => 1, :site => 'lyon', :
|
1190
|
+
# job = g5k.reserve(:nodes => 1, :site => 'lyon', :type => :deploy)
|
1139
1191
|
#
|
1140
1192
|
# begin
|
1193
|
+
# g5k.deploy(job,:env => 'wheezy-x64-base')
|
1141
1194
|
# g5k.wait_for_deploy(job,:wait_time => 100)
|
1142
1195
|
# rescue Cute::G5K::EventTimeout
|
1143
1196
|
# puts "We waited too long let's release the job"
|
@@ -1171,6 +1224,20 @@ module Cute
|
|
1171
1224
|
|
1172
1225
|
end
|
1173
1226
|
|
1227
|
+
# It returns an array of machines that did not deploy successfully
|
1228
|
+
# = Example
|
1229
|
+
# It can be used to try a new deploy:
|
1230
|
+
#
|
1231
|
+
# badnodes = g5k.check_deployment(job["deploy"].last)
|
1232
|
+
# g5k.deploy(job,:nodes => badnodes, :env => 'wheezy-x64-base')
|
1233
|
+
# g5k.wait_for_deploy(job)
|
1234
|
+
#
|
1235
|
+
# @return [Array] machines that did not deploy successfully
|
1236
|
+
# @param deploy_info [Hash] deployment structure information
|
1237
|
+
def check_deployment(deploy_info)
|
1238
|
+
deploy_info["result"].select{ |p,v| v["state"] == "KO"}.keys
|
1239
|
+
end
|
1240
|
+
|
1174
1241
|
private
|
1175
1242
|
# Handles the output of messages within the module
|
1176
1243
|
# @param msg [String] message to show
|
data/lib/cute/synchronization.rb
CHANGED
data/lib/cute/version.rb
CHANGED
data/spec/g5k_api_spec.rb
CHANGED
@@ -73,6 +73,10 @@ describe Cute::G5K::API do
|
|
73
73
|
# expect{ subject.reserve(:site => @rand_site, :resources =>"{ib30g='YES'}/nodes=2")}.to raise_error(Cute::G5K::BadRequest)
|
74
74
|
end
|
75
75
|
|
76
|
+
it "raises a bad request using OAR API" do
|
77
|
+
expect{subject.reserve(:site => @rand_site, :resources =>"nodes=1",:keys => "~/jobkey_nonexisting")}.to raise_error(Cute::G5K::BadRequest)
|
78
|
+
end
|
79
|
+
|
76
80
|
it "raises an exception at deploying" do
|
77
81
|
expect{ subject.reserve(:site => @rand_site, :nodes => 1, :env => "nonsense")}.to raise_error(Cute::G5K::RequestFailed)
|
78
82
|
end
|
@@ -126,7 +130,7 @@ describe Cute::G5K::API do
|
|
126
130
|
end
|
127
131
|
|
128
132
|
it "performs an advanced reservation" do
|
129
|
-
time_schedule = Time.now + 60*
|
133
|
+
time_schedule = Time.now + 60*10
|
130
134
|
job =subject.reserve(:site => @rand_site, :nodes => 1, :reservation => time_schedule.strftime("%Y-%m-%d %H:%M:%S"))
|
131
135
|
subject.release(job)
|
132
136
|
end
|
@@ -137,7 +141,6 @@ describe Cute::G5K::API do
|
|
137
141
|
subject.release(job)
|
138
142
|
end
|
139
143
|
|
140
|
-
|
141
144
|
it "does not deploy immediately" do
|
142
145
|
job = subject.reserve(:site => @rand_site, :type => :deploy )
|
143
146
|
expect(job).to include("types" => ["deploy"])
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,8 @@ require 'webmock/rspec'
|
|
3
3
|
|
4
4
|
SimpleCov.start
|
5
5
|
# The SimpleCov.start must be issued before any of the application code is required!
|
6
|
+
# SimpleCov hacks $:, so it needs to be re-configured here, before cute is loaded.
|
7
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
6
8
|
require 'cute'
|
7
9
|
|
8
10
|
# Disabling all external requests
|
@@ -57,6 +59,10 @@ RSpec.configure do |config|
|
|
57
59
|
with(:body => hash_including("resources" => "/slash_22=1+{nonsense},walltime=01:00")).
|
58
60
|
to_return(:status => 400, :body => "Oarsub failed: please verify your request syntax")
|
59
61
|
|
62
|
+
stub_request(:post, /^https:\/\/.*\:.*@api.grid5000.fr\/.*/).
|
63
|
+
with(:body => hash_including("import-job-key-from-file" => [ File.expand_path("~/jobkey_nonexisting") ])).
|
64
|
+
to_return(:status => 400, :body => "Oarsub failed: please verify your request syntax")
|
65
|
+
|
60
66
|
stub_request(:post, /^https:\/\/.*\:.*@api.grid5000.fr\/.*/).
|
61
67
|
with(:body => hash_including("environment" => "nonsense")).
|
62
68
|
to_return(:status => 500, :body => "Invalid environment specification")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-cute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Algorille team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -191,6 +191,7 @@ files:
|
|
191
191
|
- debian/source/format
|
192
192
|
- debian/watch
|
193
193
|
- examples/distem-bootstrap
|
194
|
+
- examples/g5k-tutorial.md
|
194
195
|
- examples/g5k_exp1.rb
|
195
196
|
- examples/g5k_exp_virt.rb
|
196
197
|
- lib/cute.rb
|
@@ -231,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
232
|
version: 1.3.6
|
232
233
|
requirements: []
|
233
234
|
rubyforge_project:
|
234
|
-
rubygems_version: 2.
|
235
|
+
rubygems_version: 2.4.5.1
|
235
236
|
signing_key:
|
236
237
|
specification_version: 4
|
237
238
|
summary: Critically Useful Tools for Experiments
|