ruby-cute 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 134cb9d25b87efcffb16969273bc4485b79a6086
4
+ data.tar.gz: b2454f6b7878e1e7339bcfd412dd52e431e17f08
5
+ SHA512:
6
+ metadata.gz: 5e73cc26463f9eafe501ba6b01f9439dcb7cb6874ee35c8ea3692797dcb75f1a73fbef9adce8df8d2ea410267a6be34a3852f968d84db9ae19b5cf3c58a444f5
7
+ data.tar.gz: d5d5bf2fa5c5d0d99944bbe010fa6456356571487be772b4f9246213a5ade9d21f60ae29b3b4813091b94529c9ef1b5c44de948512c15fac2a7bdf086339ba79
@@ -0,0 +1,6 @@
1
+ # Vi swap files
2
+ *.swp
3
+ # Generated gems
4
+ pkg/
5
+ .yardoc/
6
+ doc/
@@ -0,0 +1,2 @@
1
+ lib/**/*.rb -
2
+ examples/*.rb
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby-cute.gemspec
4
+ gemspec
5
+
6
+ gem 'simplecov', :require => false, :group => :development
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- Ruby-Cute
2
- =========
1
+ # Ruby-Cute
3
2
 
4
3
  Ruby-Cute is a set of *Commonly Used Tools for Experiments*, or *Critically
5
4
  Useful Tools for Experiments*, depending on who you ask. It is a library
@@ -7,14 +6,146 @@ aggregating various Ruby snippets useful in the context of (but not limited to)
7
6
  development of experiment software on distributed systems testbeds such as
8
7
  Grid'5000.
9
8
 
10
- Contact information
11
- -------------------
12
- Ruby-Cute is maintained by the Algorille team at LORIA/Inria Nancy - Grand Est,
13
- and specifically by:
9
+ ## Installation
10
+
11
+ From sources:
12
+
13
+ ```bash
14
+ $ git clone https://github.com/ruby-cute/ruby-cute
15
+ $ cd ruby-cute
16
+ $ gem build ruby-cute.gemspec
17
+ $ gem install ruby-cute-*.gem
18
+ ```
19
+
20
+ In Grid'5000 the installation procedure goes as follows:
21
+
22
+ ```bash
23
+ $ gem install --user-install ruby-cute
24
+ ```
25
+
26
+ Then, type the following for having ruby cute in your path (this is only necessary if you want to use interactive mode).
27
+
28
+ ```bash
29
+ $ export PATH=$PATH:$(ruby -e 'puts "#{Gem.user_dir}/bin"')
30
+ ```
31
+
32
+ ## Overview
33
+
34
+ Ruby-Cute is structured in different modules that allows you to:
35
+
36
+ - communicate with Grid'5000 through the G5K Module. For more details please refer to
37
+ [G5K Module](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/G5K/API).
38
+
39
+ - execute commands in several remote machines in parallel. Two modules are available for that:
40
+
41
+ - [Net::SSH::Multi](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Net/SSH/Multi) that uses the SSH protocol.
42
+ - [TakTuk](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/TakTuk)
43
+ which is a wrapper of [taktuk](http://taktuk.gforge.inria.fr) parallel command executor.
44
+
45
+ An example of use of Ruby-Cute in a real use case is available in
46
+ [Virtualization on Grid'5000](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/file/examples/g5k_exp_virt.rb)
47
+
48
+ ## Using pry -- an interactive ruby shell
49
+
50
+ Sometimes it may be useful to work in interactive mode. For this we can use an interactive ruby shell such as irb that is shipped by default with
51
+ Ruby, however, we highly recommend to use [pry](http://pryrepl.org/), it features syntax highlighting, method auto completion and command shell integration.
52
+ For installing pry type the following:
53
+
54
+ ```bash
55
+ $ gem install pry
56
+ ```
57
+
58
+ or, for installing in the user home directory:
59
+
60
+ ```bash
61
+ $ gem install --user-install pry
62
+ ```
63
+
64
+ When Ruby-Cute is installed, it provides a wrapper for an interactive shell that will
65
+ automatically load the necessary libraries. The following will get a *pry* prompt (if installed).
66
+
67
+ ```bash
68
+ $ cute
69
+ [1] pry(main)>
70
+ ```
71
+
72
+ The variable *$g5k* is available which can be used to access the Grid'5000 API through the
73
+ [G5K Module](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/G5K/API). For example,
74
+ let's request the name of the sites available in Grid'5000.
75
+
76
+ ```bash
77
+ [2] pry(main)> $g5k.site_uids()
78
+ => ["grenoble", "lille", "luxembourg", "lyon", "nancy", "nantes", "reims", "rennes", "sophia", "toulouse"]
79
+ ```
80
+
81
+ We can get the status of nodes in a given site by using:
82
+
83
+ ```bash
84
+ [8] pry(main)> $g5k.nodes_status("lyon")
85
+ => {"taurus-2.lyon.grid5000.fr"=>"besteffort", "taurus-16.lyon.grid5000.fr"=>"besteffort", "taurus-15.lyon.grid5000.fr"=>"besteffort", ...}
86
+ ```
87
+
88
+ Within this shell you have preloaded [G5K Module](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/G5K/API),
89
+ [Taktuk](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Cute/TakTuk) and
90
+ [Net::SSH::Multi](http://www.rubydoc.info/github/ruby-cute/ruby-cute/master/Net/SSH/Multi),
91
+ you can go directly to their respective documentation to know how to take advantage of them.
92
+
93
+ ### Experiment example
94
+
95
+ 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.
96
+ This example implements the experiment described in
97
+ [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).
98
+
99
+ ```ruby
100
+ require 'cute'
101
+ require 'net/scp'
102
+
103
+ g5k = Cute::G5K::API.new()
104
+ # We reuse a job if there is one available.
105
+ G5K_SITE = "nancy" # the chosen site has to have Infiniband 20G (e.g nancy, grenoble)
106
+ if g5k.get_my_jobs(G5K_SITE).empty?
107
+ job = g5k.reserve(:site => G5K_SITE, :resources => "{ib20g='YES'}/nodes=2/core=1",:walltime => '00:30:00', :keys => "~/my_ssh_jobkey" )
108
+ else
109
+ job = g5k.get_my_jobs(G5K_SITE).first
110
+ end
111
+
112
+ nodes = job["assigned_nodes"]
113
+
114
+ grid5000_opt = {:user => "oar", :keys => ["~/my_ssh_jobkey"], :port => 6667 }
115
+
116
+ machine_file = Tempfile.open('machine_file')
117
+
118
+ nodes.each{ |node| machine_file.puts node }
119
+
120
+ machine_file.close
121
+
122
+ netpipe ="http://pkgs.fedoraproject.org/repo/pkgs/NetPIPE/NetPIPE-3.7.1.tar.gz/5f720541387be065afdefc81d438b712/NetPIPE-3.7.1.tar.gz"
123
+
124
+ # We use the first node reserved.
125
+ Net::SCP.start(nodes.first, "oar", grid5000_opt) do |scp|
126
+ scp.upload! machine_file.path, "/tmp/machine_file"
127
+ end
128
+
129
+ Net::SSH.start(nodes.first, "oar", grid5000_opt) do |ssh|
130
+ ssh.exec!("mkdir -p netpipe_exp")
131
+ ssh.exec!("export http_proxy=\"http://proxy:3128\"; wget -O ~/netpipe_exp/NetPIPE.tar.gz #{netpipe}")
132
+ ssh.exec!("cd netpipe_exp && tar -zvxf NetPIPE.tar.gz")
133
+ ssh.exec!("cd netpipe_exp/NetPIPE-3.7.1 && make mpi")
134
+ ssh.exec("mpirun --mca plm_rsh_agent \"oarsh\" -machinefile /tmp/machine_file ~/netpipe_exp/NetPIPE-3.7.1/NPmpi")
135
+ end
136
+
137
+ g5k.release(job) # Frees resources.
138
+ ```
139
+
140
+ ## Contact information
141
+
142
+ Ruby-Cute is maintained by the Algorille team at LORIA/Inria Nancy - Grand Est, and specifically by:
143
+
14
144
  * Sébastien Badia <sebastien.badia@inria.fr>
15
145
  * Tomasz Buchert <tomasz.buchert@inria.fr>
16
146
  * Emmanuel Jeanvoine <emmanuel.jeanvoine@inria.fr>
17
147
  * Lucas Nussbaum <lucas.nussbaum@loria.fr>
18
148
  * Luc Sarzyniec <luc.sarzyniec@inria.fr>
149
+ * Cristian Ruiz <cristian.ruiz@inria.fr>
19
150
 
20
151
  Questions/comments should be directed to Lucas Nussbaum and Emmanuel Jeanvoine.
@@ -0,0 +1,48 @@
1
+ require 'rake/packagetask'
2
+ require 'rubygems/package_task'
3
+ require 'yard'
4
+ require 'rake'
5
+ require 'rspec/core/rake_task'
6
+ require 'bundler/gem_tasks'
7
+
8
+ Bundler::GemHelper.install_tasks
9
+
10
+ GEM='ruby-cute'
11
+
12
+ def get_version
13
+ Cute::VERSION
14
+ end # def:: get_version
15
+
16
+ desc "Run spec tests"
17
+
18
+ RSpec::Core::RakeTask.new(:spec) do |spec|
19
+ spec.ruby_opts = "-I lib:spec -w"
20
+ spec.pattern = 'spec/**/*_spec.rb'
21
+ end
22
+
23
+ desc "Generate source tgz package"
24
+ Rake::PackageTask::new("ruby-cute",get_version) do |p|
25
+ p.need_tar_gz = true
26
+ p.package_files.include('lib/**/*')
27
+ p.package_files.include('ext/**/*')
28
+ p.package_files.include('bin/**/*')
29
+ p.package_files.include('test/**/*')
30
+ p.package_files.include('Rakefile', 'COPYING','README', 'README.md')
31
+ end
32
+
33
+ desc "Builds a Debian package"
34
+ task :debian do
35
+ sh 'dpkg-buildpackage -us -uc'
36
+ end
37
+
38
+ desc "Builds a git snapshot package"
39
+ task :snapshot do
40
+ sh 'cp debian/changelog debian/changelog.git'
41
+ date = `date --iso=seconds |sed 's/+.*//' |sed 's/[-T:]//g'`.chomp
42
+ sh "sed -i '1 s/)/+git#{date})/' debian/changelog"
43
+ sh 'dpkg-buildpackage -us -uc'
44
+ sh 'mv debian/changelog.git debian/changelog'
45
+ end
46
+
47
+
48
+ task :default => :spec
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/ruby -w
2
+ require 'tmpdir'
3
+
4
+ ROOT_DIR= File.expand_path('../..',__FILE__)
5
+ BIN_DIR= File.join(ROOT_DIR,"bin")
6
+
7
+ if `which pry` && $?.success? then
8
+ PRY_BIN="pry"
9
+ elsif File.exist?("~/.gem/bin/pry")
10
+ PRY_BIN="~/.gem/bin/pry"
11
+ else
12
+ # We use the default ruby interactive shell
13
+ PRY_BIN="irb"
14
+ end
15
+
16
+
17
+ cute_init_file = File.open("#{Dir.mktmpdir}/cute_init.rb",'w+')
18
+ cute_init_file.puts "require 'cute'\n$g5k = Cute::G5K::API.new()"
19
+ cute_init_file.close
20
+
21
+
22
+ exec("#{PRY_BIN} -r #{cute_init_file.path}")
@@ -0,0 +1,5 @@
1
+ ruby-cute (0.0.1) UNRELEASED; urgency=low
2
+
3
+ * Initial release, as a native package for now.
4
+
5
+ -- Lucas Nussbaum <lucas@debian.org> Wed, 13 Mar 2013 10:47:08 +0100
@@ -0,0 +1 @@
1
+ 8
@@ -0,0 +1,15 @@
1
+ Source: ruby-cute
2
+ Section: ruby
3
+ Priority: optional
4
+ Maintainer: Lucas Nussbaum <lucas@debian.org>
5
+ Build-Depends: debhelper (>= 8~), gem2deb (>= 0.3.0~), rake, yard, ruby-redcloth, bc
6
+ Standards-Version: 3.9.3
7
+ Homepage: http://ruby-cute.gforge.inria.fr/
8
+ XS-Ruby-Versions: all
9
+
10
+ Package: ruby-cute
11
+ Architecture: all
12
+ XB-Ruby-Versions: ${ruby:Versions}
13
+ Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter
14
+ Description: Critically Useful Tools for Experiments
15
+
@@ -0,0 +1,33 @@
1
+ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2
+ Upstream-Name: ruby-cute
3
+ Source: FIXME <http://example.com/>
4
+
5
+ Files: *
6
+ Copyright: <years> <put author's name and email here>
7
+ <years> <likewise for another author>
8
+ License: GPL-2+ (FIXME)
9
+ This program is free software; you can redistribute it
10
+ and/or modify it under the terms of the GNU General Public
11
+ License as published by the Free Software Foundation; either
12
+ version 2 of the License, or (at your option) any later
13
+ version.
14
+ .
15
+ This program is distributed in the hope that it will be
16
+ useful, but WITHOUT ANY WARRANTY; without even the implied
17
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
18
+ PURPOSE. See the GNU General Public License for more
19
+ details.
20
+ .
21
+ You should have received a copy of the GNU General Public
22
+ License along with this package; if not, write to the Free
23
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24
+ Boston, MA 02110-1301 USA
25
+ .
26
+ On Debian systems, the full text of the GNU General Public
27
+ License version 2 can be found in the file
28
+ `/usr/share/common-licenses/GPL-2'.
29
+
30
+ Files: debian/*
31
+ Copyright: 2013 Lucas Nussbaum <lucas@debian.org>
32
+ License:
33
+ [LICENSE TEXT]
@@ -0,0 +1,2 @@
1
+ doc/
2
+ README.md
@@ -0,0 +1,2 @@
1
+ $: << 'lib' << '.'
2
+ Dir['{spec,test}/**/*.rb'].each { |f| require f }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/make -f
2
+ #export DH_VERBOSE=1
3
+ #
4
+ # Uncomment to ignore all test failures (but the tests will run anyway)
5
+ #export DH_RUBY_IGNORE_TESTS=all
6
+ #
7
+ # Uncomment to ignore some test failures (but the tests will run anyway).
8
+ # Valid values:
9
+ #export DH_RUBY_IGNORE_TESTS=ruby1.8 ruby1.9.1 require-rubygems
10
+ #
11
+ # If you need to specify the .gemspec (eg there is more than one)
12
+ #export DH_RUBY_GEMSPEC=gem.gemspec
13
+
14
+ %:
15
+ dh $@ --buildsystem=ruby --with ruby
16
+
17
+ override_dh_auto_build:
18
+ dh_auto_build
19
+ yard
@@ -0,0 +1 @@
1
+ 3.0 (native)
@@ -0,0 +1,2 @@
1
+ version=3
2
+ http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/ruby-cute .*/ruby-cute-(.*).tar.gz
@@ -0,0 +1,516 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # distem-bootstrap is a script to configure a set of nodes as distem nodes
4
+ #
5
+ ####
6
+ # distem-bootstrap is Copyright (C) 2011 Luc Sarzyniec <luc.sarzyniec@inria.fr>
7
+ # Secondary contact: Lucas Nussbaum <lucas.nussbaum@loria.fr>
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
21
+ ######
22
+ require 'rubygems'
23
+ require 'socket'
24
+ require 'tmpdir'
25
+ require 'net/scp'
26
+ require 'etc'
27
+ require 'yaml'
28
+ require 'optparse'
29
+ require 'timeout'
30
+ require 'logger'
31
+ require 'cute'
32
+
33
+ logger = Logger.new(STDOUT)
34
+
35
+ def g5k?
36
+ `hostname --fqdn`.chomp =~ /grid5000\.fr$/
37
+ end
38
+
39
+ PKG_NAME = 'distem'
40
+ DEB_REPOSITORY = 'http://distem.gforge.inria.fr/deb'
41
+ HTTP_PROXY = 'proxy:3128'
42
+ SSH_KEYS_PATH = File.join(ENV['HOME'],'.ssh')
43
+ PATH_DISTEMD_LOGS = '/var/log/distem'
44
+ TMP_DIR = '/tmp/distem'
45
+ VAR_DISTEM_NODES = 'DISTEM_NODES'
46
+ VAR_DISTEM_COORD = 'DISTEM_COORDINATOR'
47
+ STATS_PORT = 12345
48
+ if g5k?
49
+ GIT_REPOSITORY = "https://gforge.inria.fr/git/distem/distem.git"
50
+ GERRIT_REPOSITORY = "http://gerrit.nancy.grid5000.fr:8080/gerrit/p/distem"
51
+ STATS_SERV = "carol.nancy.grid5000.fr"
52
+ else
53
+ GIT_REPOSITORY = "https://gforge.inria.fr/git/distem/distem.git"
54
+ GERRIT_REPOSITORY = "https://intranet.grid5000.fr/gerrit/p/distem"
55
+ STATS_SERV = ""
56
+ end
57
+
58
+ ULIMIT_OPEN_FILES = 65535 # to be removed
59
+
60
+ pkg_utils=[
61
+ 'htop'
62
+ ]
63
+
64
+ pkg_tmp_dependencies=[]
65
+
66
+ pkg_build=[
67
+ 'git',
68
+ 'rake'
69
+ ]
70
+
71
+ pkg_build_gem=[
72
+ #'rake-compiler'
73
+ ]
74
+
75
+ options = {}
76
+ @options = options
77
+
78
+
79
+ $startt = Time::now
80
+
81
+ options[:init_pnodes] = true
82
+ options[:coordinator] = nil
83
+ options[:node_list] = nil
84
+ options[:debpackages] = []
85
+ options[:gempackages] = []
86
+ options[:git] = nil
87
+ options[:gerrit] = nil
88
+ options[:ssh_key] = nil
89
+ options[:debug] = false
90
+ options[:distem_version] = nil
91
+ options[:debug_distem] = false
92
+ options[:stats] = true
93
+ options[:git_url] = GIT_REPOSITORY
94
+ options[:verbose] = false
95
+ options[:max_vifaces] = nil
96
+ options[:cow] = nil
97
+ options[:ci] = nil
98
+ options[:network_mode] = 'classical'
99
+ options[:network_interface] = nil
100
+ options[:num_nodes] = 2
101
+ options[:walltime] = "2:00:00"
102
+
103
+ args = ARGV.dup
104
+
105
+ optparse = OptionParser.new do |opts|
106
+ opts.banner = "Usage: #{$0} [options] [<script_to_execute>]"
107
+ opts.separator ""
108
+ opts.separator "distem-bootstrap automatically sets up a distem environment."
109
+ opts.separator "When run without options, it will create a distem environment with the latest"
110
+ opts.separator "released version of distem. It will perform a reservation as follows:"
111
+ opts.separator "slash_22=1+/nodes=2,walltime=02:00 --name=distem --type=deploy"
112
+ opts.separator "and deploy the environment: \"wheezy-x64-nfs\""
113
+ opts.separator ""
114
+ opts.separator "Options:"
115
+
116
+ opts.on( '--nodes <number of nodes>', Integer,'Number of nodes to reserve on Grid5000') do |n|
117
+ options[:num_nodes] = n.to_i
118
+ end
119
+ opts.on( '-t', '--time <walltime>', 'Walltime for the reservation on Grid5000') do |n|
120
+ options[:walltime] = n
121
+ end
122
+ opts.on( '-c', '--coordinator <coordinator_address>', 'Address of the coordinator (default: first node)' ) do |c|
123
+ options[:coordinator] = c || nil
124
+ end
125
+ opts.on( '-x', '--no-init-pnodes', 'Do not initialize all pnodes' ) do |c|
126
+ options[:init_pnodes] = false
127
+ end
128
+ opts.on( '--max-vifaces <nb>', 'Set the maximum number of vifaces on a physical node (used only without --no-init-pnodes)' ) do |n|
129
+ options[:max_vifaces] = n
130
+ end
131
+ opts.on( '-d', '--debug', 'Debug mode (display commands executed on nodes)' ) do
132
+ options[:debug] = true
133
+ end
134
+ opts.on( '-D', '--distem-debug', 'Show distem output when executing distem commands (script, node init)' ) do
135
+ options[:debug_distem] = true
136
+ end
137
+ opts.on( '-k', '--ssh-key [<ssh_key_file>]', 'Path of the ssh private key to use' ) do |k|
138
+ options[:ssh_key] = k
139
+ end
140
+ opts.on( '-p', '--debpackages <package1>,<package2>,<...>', Array, 'Additional debian packages to install on coordinator' ) do |p|
141
+ options[:debpackages] = p
142
+ end
143
+ opts.on( '-r', '--gempackages <package1>,<package2>,<...>', Array, 'Additional gem packages to install on coordinator' ) do |p|
144
+ options[:gempackages] = p
145
+ end
146
+ opts.on( '--distem-version <distem_version>', 'Version of distem to install (default: latest)' ) do |v|
147
+ options[:distem_version] = v
148
+ end
149
+ opts.on( '-g', '--git [<git_hash|git_tag>]', "Install a git snapshot of distem (default: master). Additional packages are installed on the coordinator to rebuild the Debian package" ) do |n|
150
+ options[:git] = n || :git_last
151
+ end
152
+ opts.on( '-U', '--git-url <git_repository>', "Overwrite the default distem git repository" ) do |repo|
153
+ options[:git_url] = repo
154
+ end
155
+ opts.on( '-G', '--gerrit <gerrit_ref>', "Checkout a gerrit ref (e.g refs/changes/94/94/1) and rebuild a Debian package" ) do |n|
156
+ options[:gerrit] = n
157
+ end
158
+ opts.on( '-S', '--stealth-mode', 'Do not report usage statistics (Grid\'5000 only)' ) do |c|
159
+ options[:stats] = false
160
+ end
161
+ opts.on( '--btrfs-format <tmp_device>', 'Format the device with btrfs support to allow COW on Vnodes (experimental)' ) do |d|
162
+ options[:cow] = d
163
+ end
164
+ opts.on( '--ci <path>', 'Path to the distem source directory (used only for CI purpose)' ) do |path|
165
+ options[:ci] = path
166
+ end
167
+ opts.on( '-n', '--network-mode <mode>', 'Define the network mode (classical or vxlan)') do |mode|
168
+ case mode
169
+ when 'classical'
170
+ when 'vxlan'
171
+ else
172
+ puts 'Invalid network mode'
173
+ exit 1
174
+ end
175
+ options[:network_mode] = mode
176
+ end
177
+ opts.on( '-i', '--network-interface <iface>', 'Define the root network interface for inter-pnode communication (use only with vxlan network mode)') do |iface|
178
+ options[:network_interface] = iface
179
+ end
180
+ opts.on( '--verbose', 'Activate the verbose mode on Distem servers' ) do
181
+ options[:verbose] = true
182
+ end
183
+ opts.on( '-h', '--help', 'Display this screen' ) do
184
+ puts opts
185
+ exit
186
+ end
187
+ opts.separator ""
188
+ opts.separator "When executing a script, the file containing the list of nodes is available in the #{VAR_DISTEM_NODES}"
189
+ opts.separator "environment variable, while the address of the coordinator is in #{VAR_DISTEM_COORD}."
190
+ opts.separator ""
191
+ opts.separator "Examples:"
192
+ opts.separator "# run script.rb against the latest git snapshot of distem"
193
+ opts.separator "distem-bootstrap --nodes=4 --no-init-pnodes --git -- script.rb"
194
+ opts.separator "# setup distem, installing additional packages"
195
+ opts.separator "distem-bootstrap --nodes=10 -t \"04:00:00\" -p htop -r restfully -f nodes"
196
+
197
+ end
198
+
199
+ begin
200
+ optparse.parse!
201
+ rescue OptionParser::InvalidOption => e
202
+ logger.error(e.to_s)
203
+ end
204
+
205
+ logger.level = Logger::INFO
206
+ logger.level = Logger::DEBUG if options[:debug]
207
+
208
+ g5k = Cute::G5K::API.new()
209
+ g5k.logger = logger
210
+ ## performing reservation with ruby-cute
211
+
212
+ reserv_param = {:site => g5k.site, :nodes => options[:num_nodes], :name => 'distem',
213
+ :walltime => options[:walltime], :env =>'wheezy-x64-nfs', :subnets => [22,1]}
214
+
215
+ reserv_param[:keys] = options[:ssh_key] unless options[:ssh_key].nil?
216
+
217
+
218
+ # we verify if we have already submit a job
219
+ old_jobs = g5k.get_my_jobs(g5k.site).select{ |j| j["name"] == "distem"}
220
+
221
+ job = old_jobs.empty? ? g5k.reserve(reserv_param) : old_jobs.first
222
+
223
+ nodelist = job.resources["cores"].uniq
224
+ ## wait for the deploy
225
+
226
+ g5k.wait_for_deploy(job)
227
+
228
+ allnodes = nodelist.dup
229
+
230
+ # Generating ssh keys"
231
+
232
+ key_dir = Dir.mktmpdir("keys")
233
+ system "ssh-keygen -P \'\' -f #{key_dir}/keys"
234
+ logger.info "Keys generated in #{key_dir}"
235
+
236
+ nodelist.each do |node|
237
+
238
+ Net::SCP.start(node, "root") do |scp|
239
+ logger.debug "Transfering key to #{node}"
240
+ scp.upload! "#{key_dir}/keys.pub", "/root/.ssh/id_rsa.pub"
241
+ scp.upload! "#{key_dir}/keys", "/root/.ssh/id_rsa"
242
+ end
243
+
244
+ end
245
+
246
+ # Deletating keys
247
+ FileUtils.rm_rf(key_dir)
248
+
249
+ if options[:coordinator]
250
+ coordinator = options[:coordinator]
251
+ else
252
+ coordinator = nodelist[0]
253
+ end
254
+
255
+ if nodelist.include?(coordinator)
256
+ nodelist.delete(coordinator)
257
+ else
258
+ tmp = nodelist.select { |node| node =~ /^#{coordinator}/ }
259
+ if tmp.empty?
260
+ logger.warn("Coordinator not present in <nodes_file>")
261
+ else
262
+ logger.warn("Coordinator '#{coordinator}' assumed to be '#{tmp[0]}' (which is present in <nodes_file>)")
263
+ coordinator = tmp[0]
264
+ end
265
+ end
266
+
267
+
268
+ logger.info("Establishing SSH connections to all nodes")
269
+
270
+ Net::SSH::Multi.logger = logger # setting the default logger.
271
+
272
+ Net::SSH::Multi.start do |session|
273
+ nodeobjlist = {}
274
+ begin
275
+ session.group :coord do
276
+ nodeobjlist[coordinator] = session.use("root@#{coordinator}")
277
+ end
278
+ #test connection
279
+ session.with(:coord).exec! "hostname"
280
+ rescue SocketError
281
+ logger.error("can't connect to #{coordinator}")
282
+ end
283
+
284
+ begin
285
+ session.group :nodes do
286
+ nodelist.each{ |node| session.use("root@#{node}")}
287
+ end
288
+ #test connection
289
+ session.with(:nodes).exec! "hostname"
290
+ rescue SocketError
291
+ logger.error("can't connect to #{node}")
292
+ end
293
+
294
+
295
+ # Check nfs paths ## comment: this is probably unnecessary given that now we are controlling the deployment with rubycute.
296
+ tmp = session.exec! "ls -a #{ENV['HOME']}"
297
+ nopath = allnodes - tmp.keys
298
+ nopath.each do |node|
299
+ logger.warn("NFS do not seems to be mounted on #{node} (check that you have deployed with NFS env")
300
+ end
301
+
302
+ # generating ssh password less connection
303
+ session.exec! "cat .ssh/id_rsa.pub >> .ssh/authorized_keys"
304
+
305
+ if options[:cow]
306
+ logger.infor("Format #{options[:cow]} to Btrfs")
307
+ session.exec! "(umount /tmp || true) && mkfs.btrfs #{options[:cow]} && mount -o compress=lzo #{options[:cow]} /tmp && chmod 1777 /tmp"
308
+ end
309
+
310
+ #setting up ulimit-open_files, to be removed
311
+ rule="root hard nofile"
312
+ session.exec! "grep -q '#{rule}' /etc/security/limits.conf; true || echo '#{rule} #{ULIMIT_OPEN_FILES}' >> /etc/security/limits.conf"
313
+ rule="root soft nofile"
314
+ session.exec! "grep -q '#{rule}' /etc/security/limits.conf; true || echo '#{rule} #{ULIMIT_OPEN_FILES}' >> /etc/security/limits.conf"
315
+
316
+ # setup Debian repo
317
+ session.exec! "grep -q '#{DEB_REPOSITORY}' /etc/apt/sources.list || echo 'deb #{DEB_REPOSITORY} ./\ndeb-src #{DEB_REPOSITORY} ./' >> /etc/apt/sources.list"
318
+
319
+ logger.info "Updating debian packages list"
320
+ session.exec! "apt-get update -q"
321
+
322
+ if options[:git] or options[:gerrit]
323
+ logger.info("Installing debian build packages on #{coordinator}")
324
+ session.with(:coord).exec! "apt-get build-dep -y --force-yes distem"
325
+ session.with(:coord).exec! "DEBIAN_FRONTEND=noninteractive apt-get install -q -y #{pkg_build * ' '}" unless pkg_build.empty?
326
+ unless pkg_build_gem.empty?
327
+ logger.info("Installing gems build packages on #{coordinator}")
328
+ session.with(:corrd).exec! "export http_proxy=#{HTTP_PROXY}; gem install #{pkg_build_gem * ' '}"
329
+ end
330
+
331
+ gitdir = "#{TMP_DIR}/git"
332
+ session.exec! "mkdir -p #{TMP_DIR}"
333
+ session.with(:coord).exec! "rm -Rf #{gitdir}"
334
+
335
+ if options[:git]
336
+ if options[:ci] != nil
337
+ logger.info("Syncing source repository on #{coordinator} with the current Gerrit repository")
338
+ system("rsync -rlut --delete #{options[:ci]}/* root@#{options[:coordinator]}:#{gitdir}")
339
+ else
340
+ logger.info("Retrieving '#{options[:git_url]}' repository on #{coordinator}")
341
+ if g5k?
342
+ session.with(:coord).exec! "https_proxy=#{HTTP_PROXY} GIT_SSL_NO_VERIFY=1 git clone #{options[:git_url]} #{gitdir}"
343
+ else
344
+ session.with(:coord).exec! "GIT_SSL_NO_VERIFY=1 git clone #{options[:git_url]} #{gitdir}"
345
+ end
346
+ unless options[:git] == :git_last
347
+ logger.info("Setting up git repository ref:#{options[:git]} on #{coordinator}")
348
+ session.with(:corrd).exec! "git --git-dir=#{gitdir}/.git reset --hard #{options[:git]}"
349
+ end
350
+ end
351
+ else # gerrit
352
+ logger.info("Setting up git repository from gerrit ref:#{options[:gerrit]} on #{coordinator}")
353
+ session.with(:coord).exec! "git clone #{GERRIT_REPOSITORY} #{gitdir}"
354
+ session.with(:coord).exec! "cd #{gitdir} && git fetch #{GERRIT_REPOSITORY} #{options[:gerrit]} && git checkout FETCH_HEAD"
355
+ end
356
+ logger.info("Building debian package of distem on #{coordinator}")
357
+ session.with(:coord).exec! "rm -f #{TMP_DIR}/*.deb #{TMP_DIR}/*.changes #{TMP_DIR}/*.dsc #{TMP_DIR}/*.tar.gz"
358
+ session.with(:coord).exec! "cd #{gitdir}; rake snapshot"
359
+
360
+ debarchivefile = session.with(:coord).exec! "find #{TMP_DIR} -maxdepth 1 -name *.deb"
361
+
362
+ session.with(:coord).exec! "Copying generated debian package #{File.basename(debarchivefile)}"
363
+
364
+ #exec(session,"cp #{debarchivefile} #{TMP_DIR}",:coord,true)
365
+ nodelist.each {|node| session.with(:coord).exec! "scp -o StrictHostKeyChecking=no #{debarchivefile} root@#{node}:#{TMP_DIR}"}
366
+ logger.info("Installing generated debian package #{File.basename(debarchivefile)}")
367
+ depends = session.with(:coord).exec! "dpkg -I #{debarchivefile} | grep 'Depends:'"
368
+
369
+ depends = depends.split("|").last.gsub!(' ','')
370
+ session.exec! "dpkg --ignore-depends #{depends} -i #{TMP_DIR}/#{File.basename(debarchivefile)}"
371
+
372
+ session.exec! "DEBIAN_FRONTEND=noninteractive apt-get install -q -y --force-yes -f"
373
+ logger.info("Cleaning installation files on #{coordinator}")
374
+ session.with(:coord).exec! "rm -Rf #{gitdir}"
375
+ session.with(:coord).exec! "rm -f #{TMP_DIR}/*.deb #{TMP_DIR}/*.changes #{TMP_DIR}/*.dsc #{TMP_DIR}/*.tar.gz"
376
+ else
377
+ logger.info("Installing debian distem packages")
378
+ distempkg = options[:distem_version] ? "#{PKG_NAME}=#{options[:distem_version]}":PKG_NAME
379
+ session.exec! "DEBIAN_FRONTEND=noninteractive apt-get install -q -y --allow-unauthenticated #{distempkg}"
380
+ end
381
+
382
+ logger.info("Installing debian misc packages")
383
+ session.exec! "DEBIAN_FRONTEND=noninteractive apt-get install -q -y #{pkg_tmp_dependencies * ' '}" unless pkg_tmp_dependencies.empty?
384
+ session.exec! "DEBIAN_FRONTEND=noninteractive apt-get install -q -y #{pkg_utils * ' '}" unless pkg_utils.empty?
385
+ options[:debpackages].each do |debpkg|
386
+ logger.info("Installing additional debian package '#{debpkg}'")
387
+ session.exec! "DEBIAN_FRONTEND=noninteractive apt-get install -q -y #{debpkg}"
388
+ end
389
+ options[:gempackages].each do |gempkg|
390
+ logger.info "Installing additional gem package '#{gempkg}'"
391
+ session.with(:coord).exec! "export http_proxy=#{HTTP_PROXY}; gem install #{gempkg}"
392
+ end
393
+
394
+ if options[:init_pnodes] or ARGV[0]
395
+
396
+ launched = session.exec! "lsof -Pnl -i4 | egrep ':4567 |:4568'"
397
+ launched_stdout = launched.values.select{ |x| x[:stdout] }
398
+
399
+ unless launched_stdout.empty?
400
+ tokill = launched.keys
401
+
402
+ session.group :tokill do
403
+ tokill.each{ |node| session.use "root@#{node}"
404
+ logger.debug("Distem detected on #{node}")
405
+ }
406
+ end
407
+ tokill.collect!{ |node| nodeobjlist[node] }
408
+ tokill.each { |node|
409
+ logger.info("Killing previous run of distem on #{node}")
410
+ }
411
+ session.with(:tokill).exec! "killall distemd"
412
+ time_start = Time.now
413
+ until !launched_stdout or launched_stdout.empty?
414
+ session.with(:tokill).exec! 'kill -9 `ps aux|grep "distemd"|grep -v grep|sed "s/ \{1,\}/ /g"|cut -f 2 -d" "`' if (Time.now - time_start) > 2
415
+ launched = session.with(:tokill).exec! "lsof -Pnl -i4 | egrep ':4567 |:4568 '"
416
+ launched_stdout = launched.values.select{ |x| x[:stdout] }
417
+
418
+ if launched_stdout and !launched_stdout.empty?
419
+ tokill = launched.keys
420
+ tokill.collect!{ |node| nodeobjlist[node] }
421
+ end
422
+ end
423
+ end
424
+ logger.info("Starting coordinator daemon on #{coordinator}")
425
+ session.with(:coord).exec! "mkdir -p #{PATH_DISTEMD_LOGS}"
426
+ begin
427
+ Timeout.timeout(10) do
428
+ root_iface_opt = options[:network_interface] ? "--network-interface #{options[:network_interface]}" : ''
429
+ distem_cmd = "LANG=C distemd #{options[:verbose] ? '--verbose' : ''} --network-mode "+
430
+ "#{options[:network_mode]} #{root_iface_opt} -d &>#{File.join(PATH_DISTEMD_LOGS,'distemd.log')}&"
431
+
432
+ session.with(:coord).exec! distem_cmd
433
+
434
+ [ '4567', '4568' ].each do |port|
435
+ launched = []
436
+ until launched and !launched.empty?
437
+ launched = session.with(:coord).exec! "lsof -Pnl -i4 | grep ':#{port} '"
438
+ launched[coordinator][:stdout]
439
+ sleep(1)
440
+ end
441
+ end
442
+ end
443
+ rescue Timeout::Error
444
+ logger.error("Timeout reached")
445
+ end
446
+
447
+ if options[:init_pnodes]
448
+ begin
449
+ Timeout.timeout(180) do
450
+ logger.info("Initializing node #{coordinator}")
451
+ if options[:max_vifaces]
452
+ session.with(:coord).exec! "distem --coordinator host=#{coordinator} --init-pnode #{coordinator} --max-vifaces #{options[:max_vifaces]}"
453
+ # options[:debug_distem],options[:debug_distem]) this would be controlled using the logger.
454
+ else
455
+ session.with(:coord).exec! "distem --coordinator host=#{coordinator} --init-pnode #{coordinator}" # the same for this I have to add debug
456
+ end
457
+ if nodelist.length > 0
458
+ logger.info("Initializing nodes #{nodelist.join(',')}")
459
+ if options[:max_vifaces]
460
+ session.with(:coord).exec! "distem --coordinator host=#{coordinator} --init-pnode #{nodelist.join(',')} --max-vifaces #{options[:max_vifaces]}"
461
+ else
462
+ session.with(:coord).exec! "distem --coordinator host=#{coordinator} --init-pnode #{nodelist.join(',')}"
463
+ end
464
+ end
465
+ end
466
+ rescue Timeout::Error
467
+ logger.error("Timeout reached")
468
+ end
469
+ end
470
+
471
+ end
472
+
473
+ logger.info('Install done')
474
+
475
+ if ARGV[0]
476
+ begin
477
+ File.open(ARGV[0], 'r') do |f|
478
+ filename = session.with(:coord).exec!('tempfile')[coordinator][0]
479
+ logger.info("Copying script file in '#{filename}' on #{coordinator}")
480
+ `scp #{ARGV[0]} root@#{coordinator}:#{filename}`
481
+ session.with(:coord).exec! "chmod +x #{filename}"
482
+ logger.info("Executing script file '#{filename}' on #{coordinator}")
483
+
484
+ argv_dup = ARGV.dup
485
+ argv_dup.shift
486
+ script_args = (argv_dup.length > 0 ? argv_dup.join(" ") : "")
487
+ session.with(:coord).exec! "export #{VAR_DISTEM_NODES}='#{(nodelist + [coordinator]) * "\n"}'; export #{VAR_DISTEM_COORD}='#{coordinator}';#{filename} #{script_args}"
488
+ session.with(:coord).exec! "rm #{filename}"
489
+ end
490
+ logger.info 'Script execution done'
491
+ rescue Errno::ENOENT
492
+ logger.error "script file '#{ARGV[0]}' not found"
493
+ end
494
+ end
495
+
496
+ logger.info("Coordinator: #{coordinator}") if options[:init_pnodes] or ARGV[0]
497
+
498
+ # Send stats record to stats server
499
+ if g5k? and options[:stats]
500
+ begin
501
+ stats = {
502
+ :time => Time.now.to_i,
503
+ :site => Socket.gethostname,
504
+ :user => ENV['USER'],
505
+ :oar => ENV['OAR_JOB_ID'].to_i,
506
+ :nodes => allnodes,
507
+ :params => args * ' ',
508
+ :length => (Time.now - $startt).to_i
509
+ }
510
+ sock = TCPSocket.open(STATS_SERV, STATS_PORT)
511
+ sock.send(stats.to_yaml,0)
512
+ sock.close
513
+ rescue SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EHOSTUNREACH
514
+ end
515
+ end
516
+ end