ruby-cute 0.5 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|