ruby-cute 0.0.1 → 0.0.2

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.
@@ -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