ruby-cute 0.5 → 0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/debian/changelog +6 -0
- data/examples/g5k-tutorial.md +5 -5
- data/lib/cute/g5k_api.rb +149 -65
- data/lib/cute/version.rb +1 -1
- data/spec/g5k_api_spec.rb +7 -3
- data/spec/spec_helper.rb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bee03262cac39274dd43426589fce86de6962eb
|
4
|
+
data.tar.gz: b0262a03d84c76f2557550f4a0c05bcefc7c2a09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2241ac02410656a7dbf16006d2f28ea9d02767e1b4e395fc0861798e6891eb60d60ede93b7899d87f86fc3dbe355da0071a8e591760f828b5a6e14737f8011b2
|
7
|
+
data.tar.gz: ab9c2570faf491c33759ecd1317cf574b3e4779b86df251986b2456d50ba3b22eac475bc9af04e2017d278044f83bf0497f58ac6fb602ecc0be72f8f643f5a4e
|
data/README.md
CHANGED
@@ -27,6 +27,21 @@ Then, type the following for having ruby cute in your path (this is only necessa
|
|
27
27
|
```bash
|
28
28
|
$ export PATH=$PATH:$(ruby -e 'puts "#{Gem.user_dir}/bin"')
|
29
29
|
```
|
30
|
+
If you want to use Ruby-Cute outside Grid'5000 you need to create a configuration file with your credentials.
|
31
|
+
By default you need to create a file called *.grid5000_api.yml* located in your home directory:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
|
35
|
+
$ cat > ~/.grid5000_api.yml << EOF
|
36
|
+
uri: https://api.grid5000.fr/
|
37
|
+
username: user
|
38
|
+
password: **********
|
39
|
+
version: sid
|
40
|
+
EOF
|
41
|
+
|
42
|
+
```
|
43
|
+
|
44
|
+
For more details have a look at [G5K Module](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/G5K/API).
|
30
45
|
|
31
46
|
## Overview
|
32
47
|
|
data/debian/changelog
CHANGED
data/examples/g5k-tutorial.md
CHANGED
@@ -199,8 +199,8 @@ A hash is returned containing all the information about the job that we have jus
|
|
199
199
|
"directory"=>"/home/cruizsanabria",
|
200
200
|
"events"=>[],
|
201
201
|
"links"=>
|
202
|
-
[{"rel"=>"self", "href"=>"/
|
203
|
-
{"rel"=>"parent", "href"=>"/
|
202
|
+
[{"rel"=>"self", "href"=>"/3.0/sites/nancy/jobs/692665", "type"=>"application/vnd.grid5000.item+json"},
|
203
|
+
{"rel"=>"parent", "href"=>"/3.0/sites/nancy", "type"=>"application/vnd.grid5000.item+json"}],
|
204
204
|
"resources_by_type"=>{"cores"=>["graphene-67.nancy.grid5000.fr", "graphene-45.nancy.grid5000.fr"]},
|
205
205
|
"assigned_nodes"=>["graphene-67.nancy.grid5000.fr", "graphene-45.nancy.grid5000.fr"]}
|
206
206
|
|
@@ -751,7 +751,7 @@ In this variable, we can find information related with the deployment.
|
|
751
751
|
[44] pry(main)> jobs["nancy"]["deploy"]
|
752
752
|
=> [{"created_at"=>1450439620,
|
753
753
|
"environment"=>"jessie-x64-min",
|
754
|
-
"key"=>"https://api.grid5000.fr/
|
754
|
+
"key"=>"https://api.grid5000.fr/3.0/sites/nancy/files/cruizsanabria-key-84f3f1dbb1279bc1bddcd618e26c960307d653c5",
|
755
755
|
"nodes"=>["graphite-4.nancy.grid5000.fr"],
|
756
756
|
"result"=>{"graphite-4.nancy.grid5000.fr"=>{"macro"=>nil, "micro"=>nil, "state"=>"OK"}},
|
757
757
|
"site_uid"=>"nancy",
|
@@ -761,8 +761,8 @@ In this variable, we can find information related with the deployment.
|
|
761
761
|
"user_uid"=>"cruizsanabria",
|
762
762
|
"vlan"=>14,
|
763
763
|
"links"=>
|
764
|
-
[{"rel"=>"self", "href"=>"/
|
765
|
-
{"rel"=>"parent", "href"=>"/
|
764
|
+
[{"rel"=>"self", "href"=>"/3.0/sites/nancy/deployments/D-b026879e-b185-4e20-8bc5-ea0842a6954b", "type"=>"application/vnd.grid5000.item+json"},
|
765
|
+
{"rel"=>"parent", "href"=>"/3.0/sites/nancy", "type"=>"application/vnd.grid5000.item+json"}]}]
|
766
766
|
|
767
767
|
Some important information are: the status of the whole process and the state per node.
|
768
768
|
We can use this information to check if the deployment have finished successfully in all nodes.
|
data/lib/cute/g5k_api.rb
CHANGED
@@ -116,8 +116,8 @@ module Cute
|
|
116
116
|
# "properties"=>"(deploy = 'YES') AND maintenance = 'NO'",
|
117
117
|
# "directory"=>"/home/name",
|
118
118
|
# "events"=>[],
|
119
|
-
# "links"=>[{"rel"=>"self", "href"=>"/
|
120
|
-
# {"rel"=>"parent", "href"=>"/
|
119
|
+
# "links"=>[{"rel"=>"self", "href"=>"/3.0/sites/nancy/jobs/604692", "type"=>"application/vnd.grid5000.item+json"},
|
120
|
+
# {"rel"=>"parent", "href"=>"/3.0/sites/nancy", "type"=>"application/vnd.grid5000.item+json"}],
|
121
121
|
# "resources_by_type"=>
|
122
122
|
# {"cores"=>
|
123
123
|
# ["griffon-8.nancy.grid5000.fr",
|
@@ -137,7 +137,7 @@ module Cute
|
|
137
137
|
# "deploy"=>
|
138
138
|
# {"created_at"=>1423575401,
|
139
139
|
# "environment"=>"http://public.sophia.grid5000.fr/~nniclausse/openmx.dsc",
|
140
|
-
# "key"=>"https://api.grid5000.fr/
|
140
|
+
# "key"=>"https://api.grid5000.fr/3.0/sites/nancy/files/cruizsanabria-key-84f3f1dbb1279bc1bddcd618e26c960307d653c5",
|
141
141
|
# "nodes"=>["griffon-8.nancy.grid5000.fr", "griffon-9.nancy.grid5000.fr", "griffon-77.nancy.grid5000.fr"],
|
142
142
|
# "site_uid"=>"nancy",
|
143
143
|
# "status"=>"processing",
|
@@ -146,7 +146,7 @@ module Cute
|
|
146
146
|
# "user_uid"=>"cruizsanabria",
|
147
147
|
# "vlan"=>5,
|
148
148
|
# "links"=>
|
149
|
-
# [{"rel"=>"self", "href"=>"/
|
149
|
+
# [{"rel"=>"self", "href"=>"/3.0/sites/nancy/deployments/D-751096de-0c33-461a-9d27-56be1b2dd980", "type"=>"application/vnd.grid5000.item+json"},
|
150
150
|
class G5KJSON < Hash
|
151
151
|
|
152
152
|
def items
|
@@ -259,8 +259,8 @@ module Cute
|
|
259
259
|
def delete_json(path)
|
260
260
|
begin
|
261
261
|
return resource(path).delete()
|
262
|
-
rescue
|
263
|
-
|
262
|
+
rescue => e
|
263
|
+
handle_exception(e)
|
264
264
|
end
|
265
265
|
end
|
266
266
|
|
@@ -281,7 +281,15 @@ module Cute
|
|
281
281
|
end
|
282
282
|
|
283
283
|
# Issues a Cute::G5K exception according to the http status code
|
284
|
-
def handle_exception(e)
|
284
|
+
def handle_exception(e, req = nil)
|
285
|
+
puts("Error: #{$!}")
|
286
|
+
puts("Backtrace:\n\t"+e.backtrace.join("\n\t"))
|
287
|
+
if e.respond_to? :http_code
|
288
|
+
puts("HTTP Code: #{e.http_code}")
|
289
|
+
end
|
290
|
+
if e.respond_to? :response and e.response != ''
|
291
|
+
puts("Response: #{e.response}")
|
292
|
+
end
|
285
293
|
|
286
294
|
unless e.respond_to? :http_code
|
287
295
|
raise e
|
@@ -309,18 +317,17 @@ module Cute
|
|
309
317
|
|
310
318
|
# This class helps you to access Grid'5000 REST API.
|
311
319
|
# Thus, the most common actions such as reservation of nodes and deployment can be easily scripted.
|
312
|
-
#
|
320
|
+
# If you are using this module outside Grid'5000 you should created a configuration file with
|
321
|
+
# the following contents:
|
313
322
|
#
|
314
323
|
# $ cat > ~/.grid5000_api.yml << EOF
|
315
324
|
# uri: https://api.grid5000.fr/
|
316
325
|
# username: user
|
317
326
|
# password: **********
|
318
|
-
# version:
|
327
|
+
# version: stable
|
319
328
|
# EOF
|
320
329
|
#
|
321
|
-
#
|
322
|
-
# You can take a look at the {Cute::G5K::API#initialize G5K::API constructor} to see more details of
|
323
|
-
# this configuration.
|
330
|
+
# You can take a look at the {Cute::G5K::API#initialize G5K::API constructor} to see more details of this configuration.
|
324
331
|
#
|
325
332
|
# = Getting started
|
326
333
|
#
|
@@ -368,9 +375,11 @@ module Cute
|
|
368
375
|
# you can specify another path for your keys with the option *:keys*.
|
369
376
|
# In order to deploy your own environment, you have to put the tar file that contains the operating system you want to deploy and
|
370
377
|
# the environment description file, under the public directory of a given site.
|
371
|
-
# *VLANS* are supported by adding the parameter :vlan => type where type can be:
|
378
|
+
# *VLANS* are supported by adding the parameter :vlan => type where type can be: *kavlan*, *kavlan-global*, *kavlan-local*.
|
372
379
|
# The following example, reserves 10 nodes in the Lille site, starts the deployment of a custom environment over the nodes
|
373
|
-
# and puts the nodes under a routed VLAN.
|
380
|
+
# and puts the nodes under a routed VLAN. The different types of VLANs are described
|
381
|
+
# in {https://www.grid5000.fr/mediawiki/index.php/KaVLAN KaVLAN}
|
382
|
+
# We used the method {Cute::G5K::API#get_vlan_nodes get_vlan_nodes} to get the
|
374
383
|
# new hostnames assigned to your nodes.
|
375
384
|
#
|
376
385
|
# require 'cute'
|
@@ -378,12 +387,14 @@ module Cute
|
|
378
387
|
# g5k = Cute::G5K::API.new()
|
379
388
|
#
|
380
389
|
# job = g5k.reserve(:site => "lille", :nodes => 10,
|
381
|
-
# :env => '
|
382
|
-
# :vlan =>
|
390
|
+
# :env => 'http://public.lyon.grid5000.fr/~user/debian_custom_img.yaml',
|
391
|
+
# :vlan => "kavlan", :keys => "~/my_ssh_key")
|
383
392
|
#
|
384
393
|
#
|
385
394
|
# puts "Log in into the nodes using the following hostnames: #{g5k.get_vlan_nodes(job)}"
|
386
395
|
#
|
396
|
+
# You should make sure when using custom environments that in the Kadeploy description file, you use an URL to specify the path to the tarball.
|
397
|
+
# Otherwise, you will get the error 'Invalid client's export'.
|
387
398
|
# If you do not want that the method {Cute::G5K::API#reserve reserve} perform the deployment for you, you have to use the option :type => :deploy.
|
388
399
|
# This can be useful when deploying different environments in your reserved nodes, for example: deploying the environments for a small HPC cluster.
|
389
400
|
# You have to use the method {Cute::G5K::API#deploy deploy} for performing the deploy.
|
@@ -401,8 +412,8 @@ module Cute
|
|
401
412
|
# slaves = nodes[1..4]
|
402
413
|
# master = nodes-slaves
|
403
414
|
#
|
404
|
-
# g5k.deploy(job,:nodes => master, :env => '
|
405
|
-
# g5k.deploy(job,:nodes => slaves, :env => '
|
415
|
+
# g5k.deploy(job,:nodes => master, :env => 'http://public.lyon.grid5000.fr/~user/debian_master_img.yaml')
|
416
|
+
# g5k.deploy(job,:nodes => slaves, :env => 'http://public.lyon.grid5000.fr/~user/debian_slaves_img.yaml')
|
406
417
|
#
|
407
418
|
# g5k.wait_for_deploy(job)
|
408
419
|
#
|
@@ -450,6 +461,14 @@ module Cute
|
|
450
461
|
#
|
451
462
|
# [12] pry(main)> $g5k.release(job)
|
452
463
|
# [13] pry(main)> $g5k.release_all("grenoble")
|
464
|
+
# == How to debug
|
465
|
+
#
|
466
|
+
# There are two ways for debugging: (1) by using the option *:debug* when initializing the {Cute::G5K::API#initialize G5K::API} object or
|
467
|
+
# (2) by enabling the RestClient's logging:
|
468
|
+
#
|
469
|
+
# RESTCLIENT_LOG=stdout cute
|
470
|
+
# RESTCLIENT_LOG=stdout path/to/my/script
|
471
|
+
#
|
453
472
|
class API
|
454
473
|
|
455
474
|
# Assigns a logger
|
@@ -480,6 +499,15 @@ module Cute
|
|
480
499
|
#
|
481
500
|
# g5k = Cute::G5K::API.new(:on_error => :ignore)
|
482
501
|
#
|
502
|
+
# To activate debugging mode you can use the option *:debug*:
|
503
|
+
#
|
504
|
+
# g5k = Cute::G5K::API.new(:debug => true)
|
505
|
+
#
|
506
|
+
# This will provide you with a *curl* command to try by hand the same request the library is trying to perform.
|
507
|
+
# For example:
|
508
|
+
#
|
509
|
+
# 2016-09-27 11:25:34.039 => CMD debug: curl -kn https://api.grid5000.fr/3.0/sites/nancy/deployments/?user=cruizsanabria
|
510
|
+
#
|
483
511
|
# @param [Hash] params Contains initialization parameters.
|
484
512
|
# @option params [String] :conf_file Path for configuration file
|
485
513
|
# @option params [String] :uri REST API URI to contact
|
@@ -487,6 +515,7 @@ module Cute
|
|
487
515
|
# @option params [String] :username Username to access the REST API
|
488
516
|
# @option params [String] :password Password to access the REST API
|
489
517
|
# @option params [Symbol] :on_error Set to :ignore if you want to ignore {Cute::G5K::RequestFailed ResquestFailed} exceptions.
|
518
|
+
# @option params [Boolean] :debug Activate the debug mode
|
490
519
|
def initialize(params={})
|
491
520
|
config = {}
|
492
521
|
default_file = "#{ENV['HOME']}/.grid5000_api.yml"
|
@@ -504,6 +533,7 @@ module Cute
|
|
504
533
|
@uri = params[:uri] || config["uri"] || "https://api.grid5000.fr/"
|
505
534
|
@api_version = params[:version] || config["version"] || "stable"
|
506
535
|
@logger = nil
|
536
|
+
@debug = params[:debug] || false
|
507
537
|
|
508
538
|
begin
|
509
539
|
@g5k_connection = G5KRest.new(@uri,@api_version,@user,@pass,params[:on_error])
|
@@ -524,7 +554,7 @@ module Cute
|
|
524
554
|
end
|
525
555
|
|
526
556
|
# It returns the site name. Example:
|
527
|
-
#
|
557
|
+
# site #=> "rennes"
|
528
558
|
# This will only work when {Cute::G5K::API G5K::API} is used within Grid'5000.
|
529
559
|
# In the other cases it will return *nil*
|
530
560
|
# @return [String] the site name where the method is called on
|
@@ -534,7 +564,15 @@ module Cute
|
|
534
564
|
res[1] unless res.nil?
|
535
565
|
end
|
536
566
|
|
537
|
-
#
|
567
|
+
# It returns the RestClient::Resource object which provides you the *get_json* and *post_json* methods. This enables to perform low level REST requests.
|
568
|
+
# This method is intended to be used along with the {G5K::API#api_uri api_uri} method for generating valid URI.
|
569
|
+
#
|
570
|
+
# = Example:
|
571
|
+
# require 'cute'
|
572
|
+
#
|
573
|
+
# g5k = Cute::G5K::API.new()
|
574
|
+
# g5k.rest.get_json(g5k.api_uri("/sites/grenoble/clusters")
|
575
|
+
#
|
538
576
|
# @return the rest point for performing low level REST requests
|
539
577
|
def rest
|
540
578
|
@g5k_connection
|
@@ -586,6 +624,7 @@ module Cute
|
|
586
624
|
# @return [Hash] all the status information of a given Grid'5000 site
|
587
625
|
# @param site [String] a valid Grid'5000 site name
|
588
626
|
def site_status(site)
|
627
|
+
info(debug_cmd(api_uri("sites/#{site}/status"),"GET"), :debug)
|
589
628
|
@g5k_connection.get_json(api_uri("sites/#{site}/status"))
|
590
629
|
end
|
591
630
|
|
@@ -642,6 +681,7 @@ module Cute
|
|
642
681
|
# @param site [String] a valid Grid'5000 site name
|
643
682
|
# @param uid [String] user name in Grid'5000
|
644
683
|
def get_deployments(site, uid = nil)
|
684
|
+
info(debug_cmd(api_uri("sites/#{site}/deployments/?user=#{uid}"),"GET"), :debug)
|
645
685
|
@g5k_connection.get_json(api_uri("sites/#{site}/deployments/?user=#{uid}")).items
|
646
686
|
end
|
647
687
|
|
@@ -649,12 +689,14 @@ module Cute
|
|
649
689
|
# @param site [String] a valid Grid'5000 site name
|
650
690
|
# @param jid [Fixnum] a valid job identifier
|
651
691
|
def get_job(site, jid)
|
692
|
+
info(debug_cmd(api_uri("/sites/#{site}/jobs/#{jid}"),"GET"), :debug)
|
652
693
|
@g5k_connection.get_json(api_uri("/sites/#{site}/jobs/#{jid}"))
|
653
694
|
end
|
654
695
|
|
655
696
|
# @return [Hash] switches information available in a given Grid'5000 site.
|
656
697
|
# @param site [String] a valid Grid'5000 site name
|
657
698
|
def get_switches(site)
|
699
|
+
info(debug_cmd(api_uri("/sites/#{site}/network_equipments"),"GET"), :debug)
|
658
700
|
items = @g5k_connection.get_json(api_uri("/sites/#{site}/network_equipments")).items
|
659
701
|
items = items.select { |x| x['kind'] == 'switch' }
|
660
702
|
# extract nodes connected to those switches
|
@@ -688,7 +730,7 @@ module Cute
|
|
688
730
|
# get_metric("rennes")
|
689
731
|
#
|
690
732
|
# If you are only interested in the names of the available metrics:
|
691
|
-
#
|
733
|
+
# get_metric("rennes").uids #=> ["cpu_nice", "boottime", "bytes_in", ...]
|
692
734
|
#
|
693
735
|
# Then, you can get information about the probes available for a specific metric:
|
694
736
|
# get_metric("rennes",:metric => "network_in")
|
@@ -708,6 +750,7 @@ module Cute
|
|
708
750
|
params+="?"
|
709
751
|
opts[:query].each{ |k,v| params+="#{k}=#{v}&"}
|
710
752
|
end
|
753
|
+
info debug_cmd(api_uri("sites/#{site}/metrics#{params}"),"GET"), :debug
|
711
754
|
@g5k_connection.get_json(api_uri("sites/#{site}/metrics#{params}")).items
|
712
755
|
end
|
713
756
|
|
@@ -718,7 +761,7 @@ module Cute
|
|
718
761
|
# = Examples
|
719
762
|
# get_my_jobs("nancy", "waiting")
|
720
763
|
# Getting several states:
|
721
|
-
#
|
764
|
+
# get_my_jobs("nancy", ["waiting","running"])
|
722
765
|
# Valid states are specified in {https://api.grid5000.fr/doc/4.0/reference/spec.html Grid'5000 API spec}
|
723
766
|
# @return [Array] all my submitted jobs to a given site and their associated deployments.
|
724
767
|
# @param site [String] a valid Grid'5000 site name
|
@@ -828,15 +871,23 @@ module Cute
|
|
828
871
|
# If walltime is not specified, 1 hour walltime will be assigned to the reservation.
|
829
872
|
#
|
830
873
|
# job = g5k.reserve(:site => 'lille', :cluster => 'chirloute', :nodes => 2,
|
831
|
-
# :env => 'wheezy-x64-xen', :keys => "
|
874
|
+
# :env => 'wheezy-x64-xen', :keys => "path/to/my_ssh_jobkey",
|
832
875
|
# :subnets => [22,2])
|
833
876
|
#
|
877
|
+
# == Multiple types
|
878
|
+
#
|
879
|
+
# The option *:type* accepts an array for specifying multiple types.
|
880
|
+
# For example, if we want to submit a job deploy and destructive, we will type:
|
881
|
+
#
|
882
|
+
# job = g5k.reserve(:site => "nancy", :nodes => 1, :walltime => "2:00:00", :type => [:deploy,:destructive])
|
883
|
+
#
|
834
884
|
# == Before using OAR hierarchy
|
835
885
|
# All non-deploy reservations are submitted by default with the OAR option "-allow_classic_ssh"
|
836
886
|
# which does not take advantage of the CPU/core management level.
|
837
887
|
# Therefore, in order to take advantage of this capability, SSH keys have to be specified at the moment of reserving resources.
|
838
888
|
# This has to be used whenever we perform a reservation with cpu and core hierarchy.
|
839
|
-
#
|
889
|
+
# Given that OAR needs access to both keys private and public users are encouraged
|
890
|
+
# to create a pair of SSH keys for managing jobs, for instance the following command can be used:
|
840
891
|
#
|
841
892
|
# ssh-keygen -N "" -t rsa -f ~/my_ssh_jobkey
|
842
893
|
#
|
@@ -844,8 +895,11 @@ module Cute
|
|
844
895
|
# You have to specify different keys per reservation if you want several jobs running at the same time in the same site.
|
845
896
|
# Example using the OAR hierarchy:
|
846
897
|
#
|
847
|
-
# job = g5k.reserve(:site => "grenoble", :switches => 3, :nodes => 1, :cpus => 1, :cores => 1, :keys => "
|
898
|
+
# job = g5k.reserve(:site => "grenoble", :switches => 3, :nodes => 1, :cpus => 1, :cores => 1, :keys => "path/to/my_ssh_jobkey")
|
848
899
|
#
|
900
|
+
# Remember that the path passed in the *:keys* parameter corresponds to the path in the Grid'5000 site frontend where you are submitting the job.
|
901
|
+
# This is because OAR needs to access to both keys (private and public) locally.
|
902
|
+
# For deployments the path corresponds to the local path from where the script is being executed (See {Cute::G5K::API#deploy deploy} method).
|
849
903
|
# == Using OAR syntax
|
850
904
|
#
|
851
905
|
# The parameter *:resources* can be used instead of parameters such as: *:cluster*, *:nodes*, *:cpus*, *:walltime*, *:vlan*, *:subnets*, *:properties*, etc,
|
@@ -879,14 +933,15 @@ module Cute
|
|
879
933
|
# @option opts [Numeric] :nodes Number of nodes to reserve
|
880
934
|
# @option opts [String] :walltime Walltime of the reservation
|
881
935
|
# @option opts [String] :site Grid'5000 site
|
882
|
-
# @option opts [Symbol] :type Type of reservation: :deploy, :allow_classic_ssh
|
936
|
+
# @option opts [Array or Symbol] :type Type of reservation: :deploy, :allow_classic_ssh, [:deploy,:destructive]
|
883
937
|
# @option opts [String] :name Reservation name
|
884
938
|
# @option opts [String] :cmd The command to execute when the job starts (e.g. ./my-script.sh).
|
885
939
|
# @option opts [String] :cluster Valid Grid'5000 cluster
|
886
940
|
# @option opts [String] :queue A specific job queue
|
887
941
|
# @option opts [Array] :subnets 1) prefix_size, 2) number of subnets
|
888
942
|
# @option opts [String] :env Environment name for {http://kadeploy3.gforge.inria.fr/ Kadeploy}
|
889
|
-
# @option opts [
|
943
|
+
# @option opts [String] :vlan VLAN type and number: kavlan-local, kavlan, kavlan-topo, etc
|
944
|
+
# @option opts [Numeric] :num_vlan Number of VLANs
|
890
945
|
# @option opts [String] :properties OAR properties defined in the cluster
|
891
946
|
# @option opts [String] :resources OAR syntax for complex submissions
|
892
947
|
# @option opts [String] :reservation Request a job to be scheduled a specific date.
|
@@ -896,7 +951,7 @@ module Cute
|
|
896
951
|
|
897
952
|
# checking valid options
|
898
953
|
valid_opts = [:site, :cluster, :switches, :cpus, :cores, :nodes, :walltime, :cmd,
|
899
|
-
:type, :name, :subnets, :env, :vlan, :properties, :resources,
|
954
|
+
:type, :name, :subnets, :env, :vlan, :num_vlan,:properties, :resources,
|
900
955
|
:reservation, :wait, :keys, :queue, :env_user]
|
901
956
|
unre_opts = opts.keys - valid_opts
|
902
957
|
raise ArgumentError, "Unrecognized option #{unre_opts}" unless unre_opts.empty?
|
@@ -920,15 +975,18 @@ module Cute
|
|
920
975
|
type = [type] if type.is_a?(Symbol)
|
921
976
|
keys = opts[:keys]
|
922
977
|
queue = opts[:queue]
|
978
|
+
vlan = opts[:vlan]
|
979
|
+
num_vlan = opts.fetch(:num_vlan, 1)
|
980
|
+
|
981
|
+
|
982
|
+
available_vlans = nil
|
983
|
+
if opts[:vlan]
|
984
|
+
available_vlans = @g5k_connection.get_json(api_uri("sites/#{site}/vlans")).items.map{ |item| item["type"]}.uniq
|
985
|
+
available_vlans.delete("NULL")
|
986
|
+
end
|
923
987
|
|
924
|
-
vlan_opts = {:routed => "kavlan",:global => "kavlan-global",:local => "kavlan-local"}
|
925
|
-
vlan = nil
|
926
988
|
unless opts[:vlan].nil?
|
927
|
-
|
928
|
-
vlan = vlan_opts.fetch(opts[:vlan])
|
929
|
-
else
|
930
|
-
raise ArgumentError, 'Option for vlan not recognized'
|
931
|
-
end
|
989
|
+
raise ArgumentError, "VLAN type not available in site #{site}" unless available_vlans.include?(vlan)
|
932
990
|
end
|
933
991
|
|
934
992
|
raise 'At least nodes, time and site must be given' if [nodes, walltime, site].any? { |x| x.nil? }
|
@@ -950,7 +1008,7 @@ module Cute
|
|
950
1008
|
resources = (cluster.is_a?(Fixnum) ? "/cluster=#{cluster}" : "{cluster='#{cluster}'}") + resources
|
951
1009
|
end
|
952
1010
|
|
953
|
-
resources = "{type='#{vlan}'}/vlan
|
1011
|
+
resources = "{type='#{vlan}'}/vlan=#{num_vlan}+" + resources unless vlan.nil?
|
954
1012
|
resources = "slash_#{subnets[0]}=#{subnets[1]}+" + resources unless subnets.nil?
|
955
1013
|
end
|
956
1014
|
|
@@ -985,13 +1043,15 @@ module Cute
|
|
985
1043
|
begin
|
986
1044
|
# Support for the option "import-job-key-from-file"
|
987
1045
|
# The request has to be redirected to the OAR API given that Grid'5000 API
|
988
|
-
# does not support some OAR options.
|
1046
|
+
# does not support some OAR options. Bug #7360: https://intranet.grid5000.fr/bugzilla/show_bug.cgi?id=7360
|
989
1047
|
if payload['import-job-key-from-file'] then
|
990
1048
|
|
991
1049
|
temp = @g5k_connection.post_json(api_uri("sites/#{site}/internal/oarapi/jobs"),payload)
|
1050
|
+
info debug_cmd(api_uri("sites/#{site}/internal/oarapi/jobs"),"POST",payload.to_json), :debug
|
992
1051
|
sleep 1 # This is for being sure that our job appears on the list
|
993
1052
|
r = get_my_jobs(site,nil).select{ |j| j["uid"] == temp["id"] }.first
|
994
1053
|
else
|
1054
|
+
info debug_cmd(api_uri("sites/#{site}/jobs"),"POST",payload.to_json), :debug
|
995
1055
|
r = @g5k_connection.post_json(api_uri("sites/#{site}/jobs"),payload) # This makes reference to the same class
|
996
1056
|
end
|
997
1057
|
rescue Error => e
|
@@ -1004,7 +1064,17 @@ module Cute
|
|
1004
1064
|
job = @g5k_connection.get_json(r.rel_self)
|
1005
1065
|
job = wait_for_job(job) if opts[:wait] == true
|
1006
1066
|
opts.delete(:nodes) # to not collapse with deploy options
|
1007
|
-
|
1067
|
+
opts.delete(:vlan)
|
1068
|
+
opts.delete(:num_vlan)
|
1069
|
+
if opts[:env]
|
1070
|
+
if opts[:vlan]
|
1071
|
+
vlan_id = job.resources["vlans"].first
|
1072
|
+
deploy(job,opts.merge!({:vlan_id => vlan_id}))
|
1073
|
+
else
|
1074
|
+
deploy(job,opts) #type == :deploy
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
|
1008
1078
|
return job
|
1009
1079
|
|
1010
1080
|
end
|
@@ -1020,21 +1090,21 @@ module Cute
|
|
1020
1090
|
#
|
1021
1091
|
# require 'cute'
|
1022
1092
|
#
|
1023
|
-
#
|
1093
|
+
# g5k = Cute::G5K::API.new()
|
1024
1094
|
#
|
1025
|
-
#
|
1095
|
+
# sites = g5k.site_uids
|
1026
1096
|
#
|
1027
|
-
#
|
1028
|
-
#
|
1029
|
-
#
|
1030
|
-
#
|
1031
|
-
#
|
1032
|
-
#
|
1033
|
-
#
|
1034
|
-
#
|
1035
|
-
#
|
1036
|
-
#
|
1037
|
-
#
|
1097
|
+
# sites.each{ |site|
|
1098
|
+
# job = g5k.reserve(:site => site, :nodes => 5, :wait => false, :walltime => "03:00:00")
|
1099
|
+
# begin
|
1100
|
+
# job = g5k.wait_for_job(job, :wait_time => 60)
|
1101
|
+
# puts "Nodes assigned #{job['assigned_nodes']}"
|
1102
|
+
# break
|
1103
|
+
# rescue Cute::G5K::EventTimeout
|
1104
|
+
# puts "We waited too long in site #{site} let's release the job and try in another site"
|
1105
|
+
# g5k.release(job)
|
1106
|
+
# end
|
1107
|
+
# }
|
1038
1108
|
#
|
1039
1109
|
# @param job [G5KJSON] as described in {Cute::G5K::G5KJSON job}
|
1040
1110
|
# @param [Hash] opts Options
|
@@ -1097,7 +1167,7 @@ module Cute
|
|
1097
1167
|
|
1098
1168
|
# checking valid options, same as reserve option even though some option dont make any sense
|
1099
1169
|
valid_opts = [:site, :cluster, :switches, :cpus, :cores, :nodes, :walltime, :cmd,
|
1100
|
-
:type, :name, :subnets, :env, :
|
1170
|
+
:type, :name, :subnets, :env, :vlan_id, :properties, :resources,
|
1101
1171
|
:reservation, :wait, :keys, :queue, :env_user]
|
1102
1172
|
|
1103
1173
|
unre_opts = opts.keys - valid_opts
|
@@ -1136,17 +1206,14 @@ module Cute
|
|
1136
1206
|
'key' => public_key_file,
|
1137
1207
|
}
|
1138
1208
|
|
1139
|
-
if
|
1140
|
-
vlan = job.resources["vlans"].first
|
1141
|
-
payload['vlan'] = vlan
|
1142
|
-
info "Found VLAN with uid = #{vlan}"
|
1143
|
-
end
|
1209
|
+
payload['vlan'] = opts[:vlan_id] if opts[:vlan_id]
|
1144
1210
|
|
1145
1211
|
payload['user'] = opts[:env_user] unless opts[:env_user].nil?
|
1146
1212
|
|
1147
1213
|
info "Creating deployment"
|
1148
1214
|
|
1149
1215
|
begin
|
1216
|
+
info debug_cmd(api_uri("sites/#{site}/deployments"),"POST",payload.to_json), :debug
|
1150
1217
|
r = @g5k_connection.post_json(api_uri("sites/#{site}/deployments"), payload)
|
1151
1218
|
rescue Error => e
|
1152
1219
|
info "Fail to deploy"
|
@@ -1171,7 +1238,7 @@ module Cute
|
|
1171
1238
|
#
|
1172
1239
|
# = Example
|
1173
1240
|
#
|
1174
|
-
#
|
1241
|
+
# deploy_status(job, :nodes => ["adonis-10.grenoble.grid5000.fr"], :status => "terminated")
|
1175
1242
|
#
|
1176
1243
|
# @return [Array] status of deploys within a job
|
1177
1244
|
# @param job [G5KJSON] as described in {Cute::G5K::G5KJSON job}
|
@@ -1261,24 +1328,41 @@ module Cute
|
|
1261
1328
|
deploy_info["result"].select{ |p,v| v["state"] == "KO"}.keys
|
1262
1329
|
end
|
1263
1330
|
|
1331
|
+
# Returns a valid URI using the current G5K API version.
|
1332
|
+
# @return [String] valid URI
|
1333
|
+
def api_uri(path)
|
1334
|
+
path = path[1..-1] if path.start_with?('/')
|
1335
|
+
return "#{@api_version}/#{path}"
|
1336
|
+
end
|
1337
|
+
|
1264
1338
|
private
|
1265
1339
|
# Handles the output of messages within the module
|
1266
1340
|
# @param msg [String] message to show
|
1267
|
-
def info(msg)
|
1341
|
+
def info(msg,mode =nil)
|
1342
|
+
|
1343
|
+
return if mode == :debug and not @debug
|
1344
|
+
|
1268
1345
|
if @logger.nil? then
|
1269
1346
|
t = Time.now
|
1270
1347
|
s = t.strftime('%Y-%m-%d %H:%M:%S.%L')
|
1271
1348
|
puts "#{s} => #{msg}"
|
1272
1349
|
else
|
1273
|
-
|
1350
|
+
if mode == :debug
|
1351
|
+
@logger.debug(msg)
|
1352
|
+
else
|
1353
|
+
@logger.info(msg)
|
1354
|
+
end
|
1274
1355
|
end
|
1356
|
+
|
1275
1357
|
end
|
1276
1358
|
|
1277
|
-
# @return a
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1359
|
+
# @return a string containing the command line equivalent for debugging purposes
|
1360
|
+
def debug_cmd(path,method,payload=nil)
|
1361
|
+
if method == "GET"
|
1362
|
+
return "CMD debug: curl -kn #{@uri}#{path}"
|
1363
|
+
else
|
1364
|
+
return "CMD debug: curl -kni #{@uri}#{path} -X POST -H'Content-Type: application/json' -d '#{payload}'"
|
1365
|
+
end
|
1282
1366
|
end
|
1283
1367
|
|
1284
1368
|
end
|
data/lib/cute/version.rb
CHANGED
data/spec/g5k_api_spec.rb
CHANGED
@@ -3,7 +3,11 @@ require 'spec_helper'
|
|
3
3
|
# Real tests can be activated by setting the shell variable TEST_REAL
|
4
4
|
describe Cute::G5K::API do
|
5
5
|
|
6
|
-
|
6
|
+
if ENV['TEST_REAL']
|
7
|
+
subject { g5k = ENV['DEBUG'].nil?? Cute::G5K::API.new() : Cute::G5K::API.new(:debug => true) }
|
8
|
+
else
|
9
|
+
subject { g5k = ENV['DEBUG'].nil?? Cute::G5K::API.new(:user => "test") : Cute::G5K::API.new(:user => "test",:debug => true) }
|
10
|
+
end
|
7
11
|
|
8
12
|
let(:sites) { subject.site_uids}
|
9
13
|
|
@@ -99,7 +103,7 @@ describe Cute::G5K::API do
|
|
99
103
|
end
|
100
104
|
|
101
105
|
it "raises error vlan" do
|
102
|
-
expect {subject.reserve(:site => @rand_site, :vlan => :nonsense)}.to raise_error(ArgumentError,
|
106
|
+
expect {subject.reserve(:site => @rand_site, :vlan => :nonsense)}.to raise_error(ArgumentError,"VLAN type not available in site #{@rand_site}")
|
103
107
|
end
|
104
108
|
|
105
109
|
|
@@ -117,7 +121,7 @@ describe Cute::G5K::API do
|
|
117
121
|
|
118
122
|
|
119
123
|
it "reserves with vlan and get vlan hostnames" do
|
120
|
-
job = subject.reserve(:site => @rand_site, :nodes => 1, :type => :deploy, :vlan =>
|
124
|
+
job = subject.reserve(:site => @rand_site, :nodes => 1, :type => :deploy, :vlan => "kvlan")
|
121
125
|
expect(subject.get_vlan_nodes(job)).to be_an_instance_of(Array)
|
122
126
|
subject.release(job)
|
123
127
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -55,6 +55,11 @@ RSpec.configure do |config|
|
|
55
55
|
stub_request(:any,/^https:\/\/.*\:.*@api.grid5000.fr\/...\/sites\/non-found\/.*/).
|
56
56
|
to_return(:status => 404)
|
57
57
|
|
58
|
+
stub_request(:get,/^https:\/\/.*\:.*@api.grid5000.fr\/...\/sites\/.*vlans$/).
|
59
|
+
to_return(:status => 200, :body => {'total' => 3, 'items' => [{'type' => "kavlan-local"},{'type' => "kvlan"}]}.to_json)
|
60
|
+
|
61
|
+
# to_return(:status => 200, :body => {:total => 3, :items => [{:type => "kavlan-local"},{:type => "kavlan"}]})
|
62
|
+
|
58
63
|
stub_request(:post, /^https:\/\/.*\:.*@api.grid5000.fr\/.*/).
|
59
64
|
with(:body => hash_including("resources" => "/slash_22=1+{nonsense},walltime=01:00")).
|
60
65
|
to_return(:status => 400, :body => "Oarsub failed: please verify your request syntax")
|
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.6'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Algorille team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|