xp5k 0.0.6 → 0.0.7

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