ruby-cute 0.6 → 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: 4bee03262cac39274dd43426589fce86de6962eb
4
- data.tar.gz: b0262a03d84c76f2557550f4a0c05bcefc7c2a09
3
+ metadata.gz: c25c70220c9af96769ac92522d76cb09ee237846
4
+ data.tar.gz: 98596abba4e8569122cf00369fafc0b7ea8eefb1
5
5
  SHA512:
6
- metadata.gz: 2241ac02410656a7dbf16006d2f28ea9d02767e1b4e395fc0861798e6891eb60d60ede93b7899d87f86fc3dbe355da0071a8e591760f828b5a6e14737f8011b2
7
- data.tar.gz: ab9c2570faf491c33759ecd1317cf574b3e4779b86df251986b2456d50ba3b22eac475bc9af04e2017d278044f83bf0497f58ac6fb602ecc0be72f8f643f5a4e
6
+ metadata.gz: 305140e82df29d420897496719de4a71554a10ea7c9f32c90dd8c956576c7c374c6b2e57cac4daa685775631e85a6e6c23954d9686d83d4ccb3a5aa48cac459e
7
+ data.tar.gz: ae3e6bdb53792be17caeb8ee67824d4c61cb7f64ad15b29e74c099bc60a5ede6993d4051d1854e4b5826d356b1c16bf60d3679baa9ba5cee005afc18b23cb39f
data/README.md CHANGED
@@ -36,7 +36,6 @@ $ cat > ~/.grid5000_api.yml << EOF
36
36
  uri: https://api.grid5000.fr/
37
37
  username: user
38
38
  password: **********
39
- version: sid
40
39
  EOF
41
40
 
42
41
  ```
@@ -106,9 +105,86 @@ Within this shell you have preloaded [G5K Module](http://www.rubydoc.info/github
106
105
  [Net::SSH::Multi](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Net/SSH/Multi),
107
106
  you can go directly to their respective documentation to know how to take advantage of them.
108
107
 
109
- ### Experiment example
108
+ ### Examples
109
+
110
+ The following examples show how to use the [G5K Module](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/G5K/API) in an experiment.
111
+
112
+ #### Example 1: automatic experiment bootstrap
113
+
114
+ This example (also available as
115
+ [examples/xp-bootstrap](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/file/examples/xp-bootstrap)
116
+ is simple script automating the initial bootstrap of an experiment.
117
+
118
+ ```ruby
119
+ #!/usr/bin/ruby -w
120
+
121
+ # This script, to be executed on a frontend, automates the initial setup
122
+ # of an experiment, and then sleeps to let the user take over.
123
+ # The same script, run with --reserve, will handle resources reservation
124
+
125
+ # To make this work:
126
+ # - connect to a frontend
127
+ # - install ruby-cute: gem install --user-install ruby-cute
128
+ # - get this script, make it executable (chmod a+rx xp-bootstrap)
129
+ # - run it: ./xp-bootstrap --reserve
130
+
131
+ gem 'ruby-cute', ">=0.6"
132
+ require 'cute'
133
+ require 'pp'
134
+
135
+ g5k = Cute::G5K::API.new
136
+ G5K_SITE = `hostname --fqdn`.split('.')[-3] # get the site name from the `hostname` command
137
+ G5K_ENV = 'jessie-x64-base' # environment to deploy
138
+ NODES = 2
139
+ WALLTIME = '0:30'
140
+
141
+ # When the script is run with --reserve, use Ruby-Cute to reserve resources and run the script again inside the reservation, when it starts
142
+ if ARGV[0] == '--reserve'
143
+ # reserve two nodes for 30 mins
144
+ job = g5k.reserve(:site => G5K_SITE, :nodes => NODES, :walltime => WALLTIME, :type => :deploy, :wait => false,
145
+ :name => 'xp-bootstrap',
146
+ :cmd => File::realpath(__FILE__)
147
+ )
148
+ puts "Job #{job['uid']} created. Monitor its status with e.g.: oarstat -fj #{job['uid']}"
149
+ exit(0)
150
+ end
151
+
152
+ ###########################################################################
153
+ #### What follows is what gets executed inside the resources reservation
154
+
155
+ # for better output, redirect stderr to stdout, make stdout a synchronized output stream
156
+ STDERR.reopen(STDOUT)
157
+ STDOUT.sync = true
158
+
159
+ jobid = ENV['OAR_JOB_ID']
160
+ raise "OAR_JOB_ID not set. Are you running inside a OAR reservation? Maybe you should use #{__FILE__} --reserve?" if not jobid
161
+
162
+ # get job details
163
+ job = g5k.get_job(G5K_SITE, jobid)
164
+ nodes = job['assigned_nodes']
165
+ puts "Running on: #{nodes.join(' ')}"
166
+
167
+ # deploying all nodes, waiting for the end of deployment
168
+ g5k.deploy(job, :env => G5K_ENV, :wait => true)
169
+
170
+ raise "Deployment ended with error" if ((job['deploy'].last['status'] == 'error') or (not job['deploy'].last['result'].to_a.all? { |e| e[1]['state'] == 'OK' }))
171
+
172
+ cmd = 'apt-get update && apt-get -y install nuttcp'
173
+ puts "Running command: #{cmd}"
174
+ # Run a command on each node and analyze result
175
+ ssh = Net::SSH::Multi::Session::new
176
+ nodes.each { |n| ssh.use "root@#{n}" }
177
+ r = ssh.exec!(cmd)
178
+ raise "Command failed on at least one node\n#{r}" if not r.to_a.all? { |e| e[1][:status] == 0 }
179
+
180
+ # Sleep for a very long time to avoid reservation termination
181
+ puts "Experiment preparation finished."
182
+ puts "Nodes: #{nodes.join(' ')}"
183
+ sleep 86400*365
184
+ ```
185
+
186
+ #### Example 2: running MPI
110
187
 
111
- The following example shows how to use the [G5K Module](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/G5K/API) in an experiment.
112
188
  This example implements the experiment described in
113
189
  [MPI on Grid5000](https://www.grid5000.fr/mediawiki/index.php/Run_MPI_On_Grid%275000#Setting_up_and_starting_Open_MPI_to_use_high_performance_interconnect).
114
190
 
@@ -155,16 +231,16 @@ g5k.release(job) # Frees resources.
155
231
 
156
232
  ## Contact information
157
233
 
158
- Ruby-Cute is maintained by the Algorille team at LORIA/Inria Nancy - Grand Est, and specifically by:
234
+ Ruby-Cute is maintained by the Madynes team at LORIA/Inria Nancy - Grand Est, and specifically by:
159
235
 
160
- * Cristian Ruiz <cristian.ruiz@inria.fr>
161
- * Emmanuel Jeanvoine <emmanuel.jeanvoine@inria.fr>
162
236
  * Lucas Nussbaum <lucas.nussbaum@loria.fr>
163
237
 
164
238
  Past contributors include:
165
239
 
166
240
  * Sébastien Badia <sebastien.badia@inria.fr>
167
241
  * Tomasz Buchert <tomasz.buchert@inria.fr>
242
+ * Emmanuel Jeanvoine <emmanuel.jeanvoine@inria.fr>
243
+ * Cristian Ruiz <cristian.ruiz@inria.fr>
168
244
  * Luc Sarzyniec <luc.sarzyniec@inria.fr>
169
245
 
170
- Questions/comments should be directed to Lucas Nussbaum and Emmanuel Jeanvoine.
246
+ Questions/comments should be directed to Lucas Nussbaum.
@@ -1,16 +1,16 @@
1
- ruby-cute (0.6) UNRELEASED; urgency=medium
1
+ ruby-cute (0.6) unstable; urgency=medium
2
2
 
3
3
  * New release.
4
4
 
5
- -- Cristian Ruiz <cristian.ruiz@inria.fr> Thu, 20 Oct 2016 10:57:07 +0100
5
+ -- Lucas Nussbaum <lucas@debian.org> Fri, 17 Mar 2017 23:38:19 +0100
6
6
 
7
- ruby-cute (0.5) UNRELEASED; urgency=medium
7
+ ruby-cute (0.5) unstable; urgency=medium
8
8
 
9
9
  * New release.
10
10
 
11
11
  -- Cristian Ruiz <cristian.ruiz@inria.fr> Tue, 3 May 2016 08:54:42 +0100
12
12
 
13
- ruby-cute (0.4) UNRELEASED; urgency=medium
13
+ ruby-cute (0.4) unstable; urgency=medium
14
14
 
15
15
  * Initial release.
16
16
 
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ # This script, to be executed on a frontend, automates the initial setup
4
+ # of an experiment, and then sleeps to let the user take over.
5
+ # The same script, run with --reserve, will handle resources reservation
6
+
7
+ # To make this work:
8
+ # - connect to a frontend
9
+ # - install ruby-cute: gem install --user-install ruby-cute
10
+ # - get this script, make it executable (chmod a+rx xp-bootstrap)
11
+ # - run it: ./xp-bootstrap --reserve
12
+
13
+ gem 'ruby-cute', ">=0.6"
14
+ require 'cute'
15
+ require 'pp'
16
+
17
+ g5k = Cute::G5K::API.new
18
+ G5K_SITE = `hostname --fqdn`.split('.')[-3] # get the site name from the `hostname` command
19
+ G5K_ENV = 'jessie-x64-base' # environment to deploy
20
+ NODES = 2
21
+ WALLTIME = '0:30'
22
+
23
+ # When the script is run with --reserve, use Ruby-Cute to reserve resources and run the script again inside the reservation, when it starts
24
+ if ARGV[0] == '--reserve'
25
+ # reserve two nodes for 30 mins
26
+ job = g5k.reserve(:site => G5K_SITE, :nodes => NODES, :walltime => WALLTIME, :type => :deploy, :wait => false,
27
+ :name => 'xp-bootstrap',
28
+ :cmd => File::realpath(__FILE__)
29
+ )
30
+ puts "Job #{job['uid']} created. Monitor its status with e.g.: oarstat -fj #{job['uid']}"
31
+ exit(0)
32
+ end
33
+
34
+ ###########################################################################
35
+ #### What follows is what gets executed inside the resources reservation
36
+
37
+ # for better output, redirect stderr to stdout, make stdout a synchronized output stream
38
+ STDERR.reopen(STDOUT)
39
+ STDOUT.sync = true
40
+
41
+ jobid = ENV['OAR_JOB_ID']
42
+ raise "OAR_JOB_ID not set. Are you running inside a OAR reservation? Maybe you should use #{__FILE__} --reserve?" if not jobid
43
+
44
+ # get job details
45
+ job = g5k.get_job(G5K_SITE, jobid)
46
+ nodes = job['assigned_nodes']
47
+ puts "Running on: #{nodes.join(' ')}"
48
+
49
+ # deploying all nodes, waiting for the end of deployment
50
+ g5k.deploy(job, :env => G5K_ENV, :wait => true)
51
+
52
+ raise "Deployment ended with error" if ((job['deploy'].last['status'] == 'error') or (not job['deploy'].last['result'].to_a.all? { |e| e[1]['state'] == 'OK' }))
53
+
54
+ cmd = 'apt-get update && apt-get -y install nuttcp'
55
+ puts "Running command: #{cmd}"
56
+ # Run a command on each node and analyze result
57
+ ssh = Net::SSH::Multi::Session::new
58
+ nodes.each { |n| ssh.use "root@#{n}" }
59
+ r = ssh.exec!(cmd)
60
+ raise "Command failed on at least one node\n#{r}" if not r.to_a.all? { |e| e[1][:status] == 0 }
61
+
62
+ # Sleep for a very long time to avoid reservation termination
63
+ puts "Experiment preparation finished."
64
+ puts "Nodes: #{nodes.join(' ')}"
65
+ sleep 86400*365
@@ -1,5 +1,4 @@
1
1
  require 'cute/version'
2
- require 'cute/bash'
3
2
  require 'cute/execute'
4
3
  require 'cute/taktuk'
5
4
  require 'cute/g5k_api'
@@ -1,7 +1,13 @@
1
1
  require 'restclient'
2
2
  require 'yaml'
3
3
  require 'json'
4
- require 'ipaddress'
4
+ begin
5
+ old_verbose, $VERBOSE = $VERBOSE, false
6
+ require 'ipaddress'
7
+ ensure
8
+ $VERBOSE = old_verbose
9
+ end
10
+
5
11
  require 'uri'
6
12
 
7
13
  module Cute
@@ -225,14 +231,24 @@ module Cute
225
231
  return @api[path]
226
232
  end
227
233
 
234
+ RETRY_503_MAX = 10
235
+ RETRY_503_SLEEP = 1
236
+
228
237
  # @return [Hash] the HTTP response
229
238
  # @param path [String] this complements the URI to address to a specific resource
230
239
  def get_json(path)
231
-
240
+ retries = 0
232
241
  begin
233
242
  r = resource(path).get(:content_type => "application/json",
234
243
  :user_agent => @user_agent)
235
244
  rescue => e
245
+ if e.kind_of?(RestClient::Exception) and e.http_code == 503 and retries < RETRY_503_MAX
246
+ # the G5K REST API sometimes fail with error 503. In that case we should just wait and retry
247
+ puts("G5KRest: GET #{path} failed with error 503, retrying after #{RETRY_503_SLEEP} seconds")
248
+ retries += 1
249
+ sleep RETRY_503_SLEEP
250
+ retry
251
+ end
236
252
  handle_exception(e)
237
253
  end
238
254
  return G5KJSON.parse(r)
@@ -242,13 +258,20 @@ module Cute
242
258
  # @param path [String] this complements the URI to address to a specific resource
243
259
  # @param json [Hash] contains the characteristics of the resources to be created.
244
260
  def post_json(path, json)
245
-
261
+ retries = 0
246
262
  begin
247
263
  r = resource(path).post(json.to_json,
248
264
  :content_type => "application/json",
249
265
  :accept => "application/json",
250
266
  :user_agent => @user_agent)
251
267
  rescue => e
268
+ if e.kind_of?(RestClient::Exception) and e.http_code == 503 and retries < RETRY_503_MAX
269
+ # the G5K REST API sometimes fail with error 503. In that case we should just wait and retry
270
+ puts("G5KRest: POST #{path} failed with error 503, retrying after #{RETRY_503_SLEEP} seconds")
271
+ retries += 1
272
+ sleep RETRY_503_SLEEP
273
+ retry
274
+ end
252
275
  handle_exception(e)
253
276
  end
254
277
  return G5KJSON.parse(r)
@@ -257,9 +280,17 @@ module Cute
257
280
  # Deletes a resource on the server
258
281
  # @param path [String] this complements the URI to address to a specific resource
259
282
  def delete_json(path)
283
+ retries = 0
260
284
  begin
261
285
  return resource(path).delete()
262
286
  rescue => e
287
+ if e.kind_of?(RestClient::Exception) and e.http_code == 503 and retries < RETRY_503_MAX
288
+ # the G5K REST API sometimes fail with error 503. In that case we should just wait and retry
289
+ puts("G5KRest: DELETE #{path} failed with error 503, retrying after #{RETRY_503_SLEEP} seconds")
290
+ retries += 1
291
+ sleep RETRY_503_SLEEP
292
+ retry
293
+ end
263
294
  handle_exception(e)
264
295
  end
265
296
  end
@@ -616,6 +647,7 @@ module Cute
616
647
  environment_uids = environments(site).uids.map{ |e|
617
648
  e_match = /(.*)-(.*)/.match(e)
618
649
  new_name = e_match.nil? ? "" : e_match[1]
650
+ new_name
619
651
  }
620
652
 
621
653
  return environment_uids.uniq
@@ -1160,7 +1192,7 @@ module Cute
1160
1192
  # @param [Hash] opts Deploy options
1161
1193
  # @option opts [String] :env {http://kadeploy3.gforge.inria.fr/ Kadeploy} environment to deploy
1162
1194
  # @option opts [Array] :nodes Specifies the nodes to deploy on
1163
- # @option opts [String] :keys Specifies the SSH keys to copy for the deployment
1195
+ # @option opts [String] :keys Specifies the SSH keys to copy for the deployment. By default, the content of ~/.ssh/id_rsa.pub is used.
1164
1196
  # @option opts [Boolean] :wait Whether or not to wait until the deployment is done (default is false)
1165
1197
  # @return [G5KJSON] a job with deploy information as described in {Cute::G5K::G5KJSON job}
1166
1198
  def deploy(job, opts = {})
@@ -1,4 +1,9 @@
1
- require 'net/ssh/multi'
1
+ begin # load it without warnings
2
+ old_verbose, $VERBOSE = $VERBOSE, false
3
+ require 'net/ssh/multi'
4
+ ensure
5
+ $VERBOSE = old_verbose
6
+ end
2
7
  require 'logger'
3
8
 
4
9
  module Net; module SSH
@@ -91,6 +96,7 @@ module SessionActions
91
96
  #
92
97
  def exec!(command, &block)
93
98
 
99
+ Multi.logger.debug "SSH execution: #{command}"
94
100
  results = {}
95
101
 
96
102
  main =open_channel do |channel|
@@ -100,36 +106,38 @@ module SessionActions
100
106
 
101
107
  results[ch.connection.host] ||= {}
102
108
 
103
- channel.on_data do |ch, data|
109
+ channel.on_data do |c, data|
104
110
  if block
105
- block.call(ch, :stdout, data)
111
+ block.call(c, :stdout, data)
106
112
  else
107
- results[ch.connection.host][:stdout] = data.strip
108
- Multi.logger.debug("[#{ch.connection.host}] #{data.strip}")
113
+ results[c.connection.host][:stdout] ||= ""
114
+ results[c.connection.host][:stdout] += data.strip
115
+ Multi.logger.debug("[#{c.connection.host}] #{data.strip}")
109
116
  end
110
117
  end
111
- channel.on_extended_data do |ch, type, data|
118
+ channel.on_extended_data do |c, type, data|
112
119
  if block
113
- block.call(ch, :stderr, data)
120
+ block.call(c, :stderr, data)
114
121
  else
115
- results[ch.connection.host][:stderr] = data.strip
116
- Multi.logger.debug("[#{ch.connection.host}] #{data.strip}")
122
+ results[c.connection.host][:stderr] ||= ""
123
+ results[c.connection.host][:stderr] += data.strip
124
+ Multi.logger.debug("[#{c.connection.host}] #{data.strip}")
117
125
  end
118
126
  end
119
- channel.on_request("exit-status") do |ch, data|
120
- ch[:exit_status] = data.read_long
121
- results[ch.connection.host][:status] = ch[:exit_status]
122
- if ch[:exit_status] != 0
123
- Multi.logger.info("execution of '#{command}' on #{ch.connection.host}
124
- failed with return status #{ch[:exit_status].to_s}")
125
- if results[ch.connection.host][:stdout]
127
+ channel.on_request("exit-status") do |c, data|
128
+ c[:exit_status] = data.read_long
129
+ results[c.connection.host][:status] = c[:exit_status]
130
+ if c[:exit_status] != 0
131
+ Multi.logger.info("execution of '#{command}' on #{c.connection.host}
132
+ failed with return status #{c[:exit_status].to_s}")
133
+ if results[c.connection.host][:stdout]
126
134
  Multi.logger.info("--- stdout dump ---")
127
- Multi.logger.info(results[ch.connection.host][:stdout])
135
+ Multi.logger.info(results[c.connection.host][:stdout])
128
136
  end
129
137
 
130
- if results[ch.connection.host][:stderr]
138
+ if results[c.connection.host][:stderr]
131
139
  Multi.logger.info("--stderr dump ---")
132
- Multi.logger.info(results[ch.connection.host][:stderr])
140
+ Multi.logger.info(results[c.connection.host][:stderr])
133
141
  end
134
142
  end
135
143
  end
@@ -184,7 +184,7 @@ module Cute
184
184
  # Return just the value 0:(.*)
185
185
  def treat_value(string)
186
186
  tmp = string.split(":",2)
187
- value = tmp[1].nil? ? "" : tmp[1]
187
+ return tmp[1].nil? ? "" : tmp[1]
188
188
  end
189
189
 
190
190
  def to_cmd
@@ -1,3 +1,3 @@
1
1
  module Cute
2
- VERSION = "0.6"
2
+ VERSION = "0.7"
3
3
  end
@@ -23,10 +23,10 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency "yard", "~> 0.8"
24
24
  s.add_development_dependency "simplecov", "~> 0.7"
25
25
 
26
- s.add_dependency 'rest-client', '1.6.7'
27
- s.add_dependency 'json', '~> 1.8'
28
- s.add_dependency 'ipaddress', '~>0.8'
29
- s.add_dependency 'net-ssh-multi', '~>1.2'
26
+ s.add_dependency 'rest-client', '>= 1.6'
27
+ s.add_dependency 'json', '>= 1.8'
28
+ s.add_dependency 'ipaddress', '>= 0.8'
29
+ s.add_dependency 'net-ssh-multi', '>= 1.2'
30
30
 
31
31
  s.extra_rdoc_files = ['README.md', 'LICENSE']
32
32
  s.license = 'CeCILL-B'
@@ -220,4 +220,17 @@ describe Cute::G5K::API do
220
220
  expect(subject.release_all(@rand_site)).to be true
221
221
  end
222
222
 
223
+ it "retries GET automatically when there is an error" do
224
+ expect(subject.get_jobs("tmpfail")).to be_truthy
225
+ end
226
+
227
+ it "retries POST automatically when there is an error" do
228
+ job = subject.reserve(:site => 'tmpfail', :wait => false)
229
+ expect(job).to be_truthy
230
+ end
231
+
232
+ it "retries DELETE automatically when there is an error" do
233
+ job = subject.rest.delete_json("3.0/sites/tmpfail/aa")
234
+ expect(job).to be_truthy
235
+ end
223
236
  end
@@ -55,6 +55,10 @@ 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(:any,/^https:\/\/.*\:.*@api.grid5000.fr\/...\/sites\/tmpfail\/.*/).
59
+ to_return(:status => 503).
60
+ to_return(:status => 200, :body => g5k_media_type.to_json, :headers => {})
61
+
58
62
  stub_request(:get,/^https:\/\/.*\:.*@api.grid5000.fr\/...\/sites\/.*vlans$/).
59
63
  to_return(:status => 200, :body => {'total' => 3, 'items' => [{'type' => "kavlan-local"},{'type' => "kvlan"}]}.to_json)
60
64
 
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.6'
4
+ version: '0.7'
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-10-20 00:00:00.000000000 Z
11
+ date: 2017-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -112,56 +112,56 @@ dependencies:
112
112
  name: rest-client
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '='
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: 1.6.7
117
+ version: '1.6'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: 1.6.7
124
+ version: '1.6'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: json
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - "~>"
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
131
  version: '1.8'
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - "~>"
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '1.8'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: ipaddress
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - "~>"
143
+ - - ">="
144
144
  - !ruby/object:Gem::Version
145
145
  version: '0.8'
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - "~>"
150
+ - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0.8'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: net-ssh-multi
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - "~>"
157
+ - - ">="
158
158
  - !ruby/object:Gem::Version
159
159
  version: '1.2'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - "~>"
164
+ - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '1.2'
167
167
  description: Ruby library for controlling experiments
@@ -194,6 +194,7 @@ files:
194
194
  - examples/g5k-tutorial.md
195
195
  - examples/g5k_exp1.rb
196
196
  - examples/g5k_exp_virt.rb
197
+ - examples/xp-bootstrap
197
198
  - lib/cute.rb
198
199
  - lib/cute/bash.rb
199
200
  - lib/cute/configparser.rb
@@ -233,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
233
234
  version: 1.3.6
234
235
  requirements: []
235
236
  rubyforge_project:
236
- rubygems_version: 2.2.2
237
+ rubygems_version: 2.5.2
237
238
  signing_key:
238
239
  specification_version: 4
239
240
  summary: Critically Useful Tools for Experiments
@@ -245,4 +246,3 @@ test_files:
245
246
  - spec/taktuk_spec.rb
246
247
  - test/test_bash.rb
247
248
  - test/test_execute.rb
248
- has_rdoc: