xp5k 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29af43dd9e56a5562a4f327a6f54be5591ef9f98
4
- data.tar.gz: b2003bfefbaad6a7779dd396a9cc03ce85a4bc79
3
+ metadata.gz: 66269fa663c38a7f293bcea2205b809e677c1332
4
+ data.tar.gz: 80597c35a94b407e3a9944e3935c46931629e9c9
5
5
  SHA512:
6
- metadata.gz: d8b9f40bf89d6739869ca6633f813b8cb5ac0fc6f64dfd80a903de6cc0c91abb7111c5a7b0f3a7721f254270a25f59e59a888a540a52fe40bf8c9cc27804b1db
7
- data.tar.gz: 521647dc1f924275374e140371ce75fa8e93dcfc4d5ece3f9141076ffa531da6d436aa99f3b9425d40685fb9e4b98ebea83b46b4fe1d56504a876e4a1a80028c
6
+ metadata.gz: e28427dd47bdbdd3020dcd2718926ef42fc6ef02314b6123dfd7bf0f97cb2fc7f688c878e39d25fc68f6e6d3e3200aeab9013d1c3d3c0fb2419c1470fafefba3
7
+ data.tar.gz: 91024c08522a417cc600ae5aab4d00873bedf50162b18dcdb298814fb512485dc946314cff799f0a2e1ce25c898cd77a467bcdeef10cfebdd5627d8e2bcf15b1
data/LICENCE CHANGED
@@ -1,6 +1,9 @@
1
1
  Copyright (c) 2012 Pascal Morillon
2
2
  Copyright (c) 2012 Université Rennes 1
3
3
 
4
+ Contributors:
5
+ - Matthieu Simonin (INRIA)
6
+
4
7
  Permission is hereby granted, free of charge, to any person obtaining
5
8
  a copy of this software and associated documentation files (the
6
9
  "Software"), to deal in the Software without restriction, including
data/README.md CHANGED
@@ -1,82 +1,189 @@
1
- ## Capistrano sample
2
-
3
- require 'xp5k'
4
-
5
- XP5K::Config.load
6
-
7
- @myxp = XP5K::XP.new(:logger => logger)
8
-
9
- @myxp.define_job({
10
- :resources => "nodes=2,walltime=1",
11
- :site => XP5K::Config[:site] || 'rennes',
12
- :types => ["deploy"],
13
- :name => "job1",
14
- :command => "sleep 86400"
15
- })
16
-
17
- @myxp.define_job({
18
- :resources => "nodes=6,walltime=1",
19
- :site => XP5K::Config[:site] || 'rennes',
20
- :types => ["deploy"],
21
- :name => "job2",
22
- :roles => [
23
- XP5K::Role.new({ :name => 'server', :size => 1 }),
24
- XP5K::Role.new({ :name => 'nodes', :size => 5 })
25
- ]
26
- :command => "sleep 86400"
27
- })
28
-
29
- @myxp.define_deployment({
30
- :site => XP5K::Config[:site] || 'rennes',
31
- :environment => "squeeze-x64-nfs",
32
- :jobs => %w{ job1 },
33
- :roles => %w{ server }
34
- :key => File.read(XP5K::Config[:public_key])
35
- })
36
-
37
- role :job1 do
38
- @myxp.job_with_name('job1')['assigned_nodes'].first
39
- end
40
-
41
- role :job2 do
42
- @myxp.job_with_name('job2')['assigned_nodes'].first
43
- end
44
-
45
- role :nodes do
46
- @myxp.role_with_name('nodes').servers
47
- end
48
-
49
- desc 'Submit jobs'
50
- task :submit do
51
- @myxp.submit
52
- end
53
-
54
- desc 'Deploy with Kadeplopy'
55
- task :deploy do
56
- @myxp.deploy
57
- end
58
-
59
- desc 'Status'
60
- task :status do
61
- @myxp.status
62
- end
63
-
64
- desc 'Remove all running jobs'
65
- task :clean do
66
- logger.debug "Clean all Grid'5000 running jobs..."
67
- @myxp.clean
68
- end
69
-
70
- desc 'Run date command'
71
- task :date, :roles => [:job1, :job2] do
72
- set :user, 'root'
73
- run 'date'
74
- end
75
-
76
-
77
- ## _xp.conf_ sample file
78
-
79
- site 'rennes'
80
- public_key '/Users/pmorillon/.ssh/id_rsa_g5k.pub'
1
+ ## Installation
81
2
 
3
+ Add this to your application Gemfile:
82
4
 
5
+ ```ruby
6
+ gem "xp5k"
7
+ gem "capistrano", "< 3.0.0"
8
+ ```
9
+
10
+ and then run
11
+
12
+ ```ruby
13
+ bundle install
14
+ ```
15
+
16
+ Configure restfully :
17
+
18
+ ```bash
19
+ $) cat ~/.restfully/api.grid5000.fr.yml
20
+ base_uri: https://api.grid5000.fr/3.0
21
+ username: "###"
22
+ password: "###"
23
+ ```
24
+
25
+ You are now ready to use ```xp5k``` and ```capistrano```.
26
+
27
+ You will find the documentation of capistrano in the following link :
28
+ https://github.com/capistrano/capistrano/wiki
29
+
30
+ ## Sample
31
+
32
+ Here is an example of a ```Capfile``` :
33
+ ```ruby
34
+ require 'xp5k'
35
+ require 'capistrano'
36
+
37
+ set :g5k_user, "msimonin"
38
+ # gateway
39
+ set :gateway, "#{g5k_user}@access.grid5000.fr"
40
+ # These keys will used to access the gateway and nodes
41
+ ssh_options[:keys]= [File.join(ENV["HOME"], ".ssh", "id_rsa"), File.join(ENV["HOME"], ".ssh", "id_rsa_insideg5k")]
42
+ # # This key will be installed on nodes
43
+ set :ssh_public, File.join(ENV["HOME"], ".ssh", "id_rsa_insideg5k.pub")
44
+
45
+ XP5K::Config.load
46
+
47
+ @myxp = XP5K::XP.new(:logger => logger)
48
+
49
+ @myxp.define_job({
50
+ :resources => "nodes=2,walltime=1",
51
+ :site => 'rennes',
52
+ :types => ["deploy"],
53
+ :name => "job1",
54
+ :command => "sleep 86400"
55
+ })
56
+
57
+ @myxp.define_deployment({
58
+ :site => 'rennes',
59
+ :environment => "wheezy-x64-nfs",
60
+ :jobs => %w{ job1 },
61
+ :key => File.read("#{ssh_public}")
62
+ })
63
+
64
+ role :job1 do
65
+ @myxp.job_with_name('job1')['assigned_nodes'].first
66
+ end
67
+
68
+ desc 'Submit jobs'
69
+ task :submit do
70
+ @myxp.submit
71
+ @myxp.wait_for_jobs
72
+ end
73
+
74
+ desc 'Deploy with Kadeplopy'
75
+ task :deploy do
76
+ @myxp.deploy
77
+ end
78
+
79
+ desc 'Status'
80
+ task :status do
81
+ @myxp.status
82
+ end
83
+
84
+ desc 'Remove all running jobs'
85
+ task :clean do
86
+ logger.debug "Clean all Grid'5000 running jobs..."
87
+ @myxp.clean
88
+ end
89
+
90
+ desc 'Run date command'
91
+ task :date, :roles => [:job1] do
92
+ set :user, 'root'
93
+ run 'date'
94
+ end
95
+ ```
96
+
97
+ After filling the Capfile you can start working with Capistrano :
98
+
99
+ ```bash
100
+ ➤ cap -T
101
+ cap clean # Remove all running jobs
102
+ cap date # Run date command
103
+ cap deploy # Deploy with Kadeplopy
104
+ cap invoke # Invoke a single command on the remote servers.
105
+ cap shell # Begin an interactive Capistrano session.
106
+ cap status # Status
107
+ cap submit # Submit jobs
108
+ ```
109
+
110
+ For instance you can launch : ```cap submit deploy date```
111
+
112
+ ## Extra features
113
+
114
+ ### Xp5k Roles
115
+
116
+ You can define specific roles in you job submission.
117
+ This all
118
+
119
+ ```ruby
120
+ @myxp.define_job({
121
+ :resources => "nodes=6,walltime=1",
122
+ :site => XP5K::Config[:site] || 'rennes',
123
+ :types => ["deploy"],
124
+ :name => "job2",
125
+ :roles => [
126
+ XP5K::Role.new({ :name => 'server', :size => 1 }),
127
+ XP5K::Role.new({ :name => 'clients', :size => 4 }),
128
+ XP5K::Role.new({ :name => 'frontend', :size => 1 })
129
+ ],
130
+ :command => "sleep 86400"
131
+ })
132
+
133
+ @myxp.define_deployment({
134
+ :site => 'rennes',
135
+ :environment => "wheezy-x64-nfs",
136
+ :roles => %w{ server frontend },
137
+ :key => File.read("#{ssh_public}")
138
+ })
139
+
140
+ @myxp.define_deployment({
141
+ :site => 'rennes',
142
+ :environment => "squeeze-x64-nfs",
143
+ :roles => %w{ clients },
144
+ :key => File.read("#{ssh_public}")
145
+ })
146
+
147
+
148
+ role :server do
149
+ @myxp.role_with_name('server').servers
150
+ end
151
+
152
+ role :server do
153
+ @myxp.role_with_name('frontend').servers
154
+ end
155
+
156
+ role :server do
157
+ @myxp.role_with_name('clients').servers
158
+ end
159
+ ```
160
+ ### Get the deployed nodes
161
+
162
+ Some time nodes fail to be deployed. You can get the exact set
163
+ of nodes deployed in your xp5k job or role using the ```get_deployed_nodes```
164
+ method.
165
+
166
+ ```ruby
167
+ role :clients do
168
+ @myxp.get_deployed_nodes('clients')
169
+ end
170
+ ```
171
+
172
+ ### Automatic redeployment
173
+
174
+ If some nodes fail to be deployed, ```xp5k``` will by default
175
+ retry to deploy them up to 3 times.
176
+ You can control this behaviour passing special keys in the deployment hash.
177
+
178
+ ```ruby
179
+ # disable the retry
180
+ @myxp.define_deployment({
181
+ ...
182
+ :retry => true | false # enable / disable retry mechanism
183
+ # true by default
184
+ :retries => 3 # number of retries
185
+
186
+ :goal => 2 # min number of deployed nodes wanted
187
+ # can be a percentage : "80%"
188
+ })
189
+ ```
data/lib/xp5k/role.rb CHANGED
@@ -1,11 +1,16 @@
1
1
  class XP5K::Role
2
2
 
3
- attr_accessor :name, :size, :desc, :servers, :jobid
3
+ attr_accessor :name, :size, :desc, :servers, :jobid, :inner
4
+
5
+ @@roles = []
4
6
 
5
7
  def initialize(options = {})
8
+ # Defaults
9
+ @inner = false
10
+ @servers = []
11
+ @desc = ""
12
+
6
13
  # Required parameters
7
- @name = ""
8
- @size = 0
9
14
  %w{ name size }.each do |param|
10
15
  if options[param.to_sym].nil?
11
16
  raise XP5K::Exceptions::Role, "#{self.to_s}: #{param.to_sym} needed at initialization"
@@ -15,9 +20,62 @@ class XP5K::Role
15
20
  end
16
21
 
17
22
  # Optional parameters
18
- %w{ desc servers }.each do |param|
19
- instance_variable_set("@#{param}", options[param.to_sym])
23
+ %w{ desc servers inner }.each do |param|
24
+ instance_variable_set("@#{param}", options[param.to_sym]) if options[param.to_sym]
25
+ end
26
+ end
27
+
28
+ def self.create_roles(job, job_definition)
29
+ # Definition will return list of roles
30
+ roles = []
31
+
32
+ # Test if job contain enough nodes for all roles
33
+ count_needed_nodes = 0
34
+ job_definition[:roles].each { |role| count_needed_nodes += role.size if not role.inner }
35
+ if job['assigned_nodes'].length < count_needed_nodes
36
+ raise "Job ##{job['uid']} require more nodes for required roles"
37
+ end
38
+
39
+ # Sort nodes assigned to the job
40
+ available_nodes = { 'job' => job['assigned_nodes'].sort }
41
+
42
+
43
+ # Sort roles to manage inner roles at the end
44
+ defined_roles = job_definition[:roles].sort do |x,y|
45
+ a = x.inner ? 1 : 0
46
+ b = y.inner ? 1 : 0
47
+ a <=> b
20
48
  end
49
+
50
+ # Attributes nodes to roles
51
+ defined_roles.each do |role|
52
+ next if self.exists?(role.name)
53
+ if role.inner
54
+ available_nodes[role.inner] ||= self.findByName(role.inner).servers
55
+ role.servers = available_nodes[role.inner][0..(role.size - 1)]
56
+ available_nodes[role.inner] -= role.servers
57
+ else
58
+ role.servers = available_nodes['job'][0..(role.size - 1)]
59
+ available_nodes['job'] -= role.servers
60
+ end
61
+ role.jobid = job['uid']
62
+ roles << role
63
+ @@roles << role
64
+ end
65
+ return roles
66
+ end
67
+
68
+ def self.list()
69
+ @@roles
70
+ end
71
+
72
+ def self.findByName(name)
73
+ roles = @@roles.select { |x| x.name == name }
74
+ roles.empty? ? nil : roles.first
75
+ end
76
+
77
+ def self.exists?(name)
78
+ @@roles.select { |x| x.name == name }.empty? ? false : true
21
79
  end
22
80
 
23
81
  end
data/lib/xp5k/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module XP5K
2
- VERSION='0.0.6'
2
+ VERSION='0.0.7'
3
3
  end
data/lib/xp5k/xp.rb CHANGED
@@ -79,11 +79,11 @@ module XP5K
79
79
  datas = JSON.parse(File.read(".xp_cache"))
80
80
  uid = datas["jobs"].select { |x| x["name"] == job_hash[:name] }.first["uid"]
81
81
  unless uid.nil?
82
- job = @connection.root.sites[job_hash[:site].to_sym].jobs(:query => { :user => @connection.config.options[:username] })["#{uid}".to_sym]
82
+ job = @connection.root.sites[job_hash[:site].to_sym].jobs(:query => { :user => @connection.config.options[:username] || ENV['USER'] })["#{uid}".to_sym]
83
83
  if (not job.nil? or job["state"] == "running")
84
84
  j = job.reload
85
85
  self.jobs << j
86
- create_roles(j, job_hash) unless job_hash[:roles].nil?
86
+ self.roles += Role.create_roles(j, job_hash) unless job_hash[:roles].nil?
87
87
  end
88
88
  end
89
89
  # reload last deployed nodes
@@ -108,15 +108,19 @@ module XP5K
108
108
  end
109
109
 
110
110
  def wait_for_jobs
111
- logger.info "Waiting for running state"
111
+ logger.info "Waiting for running state (Ctrl+c to stop waiting)"
112
112
  ready = false
113
113
  jobs_status = []
114
+ trap("SIGINT") do
115
+ logger.info "Stop waiting job."
116
+ exit
117
+ end
114
118
  until ready
115
119
  self.jobs.each.with_index do |job, id|
116
120
  jobs_status[id] = job.reload["state"]
117
121
  case jobs_status[id]
118
122
  when "running"
119
- create_roles(job, jobs2submit[id]) unless jobs2submit[id][:roles].nil?
123
+ self.roles += Role.create_roles(job, jobs2submit[id]) unless jobs2submit[id][:roles].nil?
120
124
  logger.info "Job #{job['uid']} is running"
121
125
  when /terminated|error/
122
126
  logger.info "Job #{job['uid']} is terminated"
@@ -128,22 +132,8 @@ module XP5K
128
132
  sleep 3
129
133
  end
130
134
  update_cache()
131
- end
132
-
133
- def create_roles(job, job_definition)
134
- count_needed_nodes = 0
135
- job_definition[:roles].each { |role| count_needed_nodes += role.size }
136
- if job['assigned_nodes'].length < count_needed_nodes
137
- self.clean
138
- raise "Job ##{job['uid']} require more nodes for required roles"
139
- end
140
- available_nodes = job['assigned_nodes'].sort
141
- job_definition[:roles].each do |role|
142
- role.servers = available_nodes[0..(role.size - 1)]
143
- available_nodes -= role.servers
144
- role.jobid = job['uid']
145
- next if not self.roles.select { |x| x.name == role.name }.empty?
146
- self.roles << role
135
+ trap "SIGINT" do
136
+ raise
147
137
  end
148
138
  end
149
139
 
@@ -152,7 +142,9 @@ module XP5K
152
142
  end
153
143
 
154
144
  def role_with_name(name)
155
- self.roles.select { |x| x.name == name}.first
145
+ role = self.roles.select { |x| x.name == name}.first
146
+ logger.debug "Role #{name} not found." if role.nil?
147
+ return role
156
148
  end
157
149
 
158
150
  def get_deployed_nodes(job_or_role_name)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xp5k
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pascal Morillon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-30 00:00:00.000000000 Z
11
+ date: 2014-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: restfully
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  version: 1.3.6
107
107
  requirements: []
108
108
  rubyforge_project:
109
- rubygems_version: 2.0.14
109
+ rubygems_version: 2.1.10
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: A small Grid'5000 helper to submit jobs and deploy environments via REST