comana 0.0.10 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8203f79238f204d50f1e79f7e29bea90f20f4613
4
+ data.tar.gz: 8b3fe89b37f61a5f8f5bf0df6a346d2a0614c072
5
+ SHA512:
6
+ metadata.gz: 291a9ab5f01fa4641a40d2c917f3a6d63861777dba484a1a065f8342b5f9d2416c66f0d87d273163d2b2a02f3040346f8333210a61813e7a605d1c765e549416
7
+ data.tar.gz: 0794a0a6e80a3556e3fb7b7cb4b49a46d78746c59597e6ea194a8fe852bfaa4588431383edacc5c2a5a3225fc87385af8874d585313e11b910993f8431b9219b
data/CHANGES CHANGED
@@ -1,6 +1,13 @@
1
1
  = comana changelog
2
2
 
3
- == Master (for 0.0.11)
3
+ == Master (for 0.1.1)
4
+
5
+ == Version 0.1.0 [2016-03-04] released
6
+ - Comana::ComputationManager#start() rename to exec().
7
+ - Comana::ComputationManager.exec is added.
8
+ - Add Comana::HostInspector
9
+ - Add bin/hostinfo
10
+ - Add bin/genqsub
4
11
 
5
12
  == Version 0.0.10 [2014-08-28] released
6
13
  - Provide namespace 'Comana' for classes.
data/Gemfile CHANGED
@@ -4,17 +4,12 @@ source "http://rubygems.org"
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
6
  # Add dependencies to develop your gem here.
7
- # Include everything needed to run rake, tests, features, etc.
7
+ # Include everything needed to exec rake, tests, features, etc.
8
8
  group :development do
9
- #gem "rspec", ">= 2.10.0"
10
- gem "rdoc", "~> 4.0.1"
11
- gem "bundler", "~> 1.7.2"
12
- gem "jeweler", ">= 2.0.1"
13
- gem "simplecov", ">= 0"
14
- #gem "builder", "~> 3.2.2"
15
- #gem "nokogiri", ">= 1.5.9"
16
- #
17
- gem "tefil", ">= 0.0.3"
18
- #gem "psych", ">= 1.3.4"
19
-
9
+ gem "test-unit", "~> 3.1"
10
+ gem "rdoc", "~> 4.2"
11
+ gem "bundler", "~> 1.11"
12
+ gem "jeweler", "~> 2.0"
13
+ gem "simplecov", "~> 0.11"
14
+ gem "tefil", ">= 0.1"
20
15
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.10
1
+ 0.1.0
data/bin/genqsub ADDED
@@ -0,0 +1,45 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require "pp"
5
+ require "comana"
6
+ require "optparse"
7
+
8
+ ## option analysis
9
+ OPTIONS = {}
10
+ op = OptionParser.new
11
+ op.banner = [
12
+ "Usage: #{File.basename("#{__FILE__}")} [options] host_series command",
13
+ ].join("\n")
14
+ op.on("-s" , "--submit" , "Submit at the same time."){OPTIONS[:submit] = true}
15
+ op.on("-o file", "--outfile=file", "Output in file."){|v| OPTIONS[:outfile] = v}
16
+ op.parse!(ARGV)
17
+
18
+ io = STDOUT
19
+ io = File.open( OPTIONS[:outfile], "w") if OPTIONS[:outfile]
20
+
21
+ series = ARGV.shift
22
+
23
+ cluster_setting = Comana::ClusterSetting.load_file
24
+ settings = cluster_setting.groups[series]
25
+
26
+ unless settings
27
+ puts "Not found '#{series}' in #{cluster_setting.data_file}"
28
+ puts op.banner
29
+ exit
30
+ end
31
+ ppn = settings["ppn"]
32
+
33
+ command = ARGV.join(' ')
34
+
35
+ GridEngineScript.generate(io, series, ppn, command)
36
+ io.close
37
+
38
+ if OPTIONS[:submit]
39
+ unless OPTIONS[:outfile]
40
+ puts "'--submit' option must use with '--outfile' option."
41
+ exit
42
+ end
43
+
44
+ GridEngineScript.write_submit(series, ppn, command, OPTIONS[:outfile])
45
+ end
data/bin/hostinfo ADDED
@@ -0,0 +1,182 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require "comana"
5
+ require "optparse"
6
+ require "pp"
7
+
8
+ ##Analyze options
9
+ COMMAND_NAME = File.basename("#{__FILE__}")
10
+ OPTIONS = {}
11
+ op = OptionParser.new
12
+ op.banner = <<HERE
13
+ Usage: #{COMMAND_NAME} subcommand [options] [hosts]
14
+ Subcommands:
15
+ ping # Ping: o and x indicate OK and N/A, respectively.
16
+ alive # Ping only alive: show alive hostname
17
+ cwd # cwd of processes owned by ssh login user.
18
+ ps # Info from ps.
19
+ cpuinfo # CPU information from /proc/cpuinfo.
20
+ meminfo # memory information from /proc/meminfo.
21
+ load # the highest process
22
+ For example,
23
+ #{COMMAND_NAME} ping # for all hosts
24
+ #{COMMAND_NAME} ping host01 host02
25
+ #{COMMAND_NAME} ping -g host_series
26
+ #{COMMAND_NAME} ping -u # update cached data
27
+ HERE
28
+
29
+ op.on("-u", "--update" , "Update cache"){OPTIONS[:update] = true}
30
+ op.on("-g", "--group" , "Indicate group of hosts" ){OPTIONS[:group] = true}
31
+ op.on("-q", "--quiet" , "No output 'Wainting for...'"){OPTIONS[:quiet] = true}
32
+ op.parse!(ARGV)
33
+
34
+ ## Analyze subcommand
35
+ subcommand = ARGV.shift
36
+ requirement = {}
37
+ case subcommand
38
+ when "ping"
39
+ requirement[:ping] = true
40
+ when "alive"
41
+ requirement[:ping] = true
42
+ when "cwd"
43
+ requirement[:cwd] = true
44
+ when "ps"
45
+ requirement[:ps] = true
46
+ when "cpuinfo"
47
+ requirement[:cpuinfo] = true
48
+ when "meminfo"
49
+ requirement[:meminfo] = true
50
+ when "load"
51
+ requirement[:ps] = true
52
+ requirement[:cwd] = true
53
+ else
54
+ puts op.help
55
+ exit
56
+ end
57
+
58
+ ## Return selected hosts
59
+ def target_hosts
60
+ cs = Comana::ClusterSetting.load_file
61
+ groups = {}
62
+ cs.groups.each do |key, val|
63
+ cluster = key
64
+ groups[key] = val["members"]
65
+ end
66
+ hs = Comana::HostSelector.new groups
67
+ if OPTIONS[:group]
68
+ hosts = []
69
+ ARGV.each do |group|
70
+ begin
71
+ hosts << hs.select_group(group)
72
+ rescue Comana::HostSelector::NoEntryError
73
+ $stderr.puts "Unknown group: #{group}"
74
+ end
75
+ end
76
+ hosts.flatten!
77
+ else
78
+ hosts = ARGV
79
+ end
80
+ hosts = hs.select_all if ARGV.empty?
81
+ hosts
82
+ end
83
+
84
+ def time_ago(uptime)
85
+ if uptime
86
+ second = (Time.now - uptime).to_i
87
+ if second >= 86400
88
+ result = sprintf("%2s days ago", second / 86400)
89
+ elsif second >= 3600
90
+ result = sprintf("%2s hours ago", second / 3600)
91
+ elsif second >= 60
92
+ result = sprintf("%2s min ago", second / 60)
93
+ else
94
+ result = sprintf("%2s sec ago", second)
95
+ end
96
+ else #e.g., not exist cache file
97
+ result = "need_update"
98
+ end
99
+ return result
100
+ end
101
+
102
+ ## Collect information
103
+ hostinspectors = target_hosts.map { |host| Comana::HostInspector.new(host) }
104
+ if OPTIONS[:update]
105
+ Thread.abort_on_exception = true
106
+ threads = {}
107
+ hostinspectors.each do |hi|
108
+ threads[hi] = Thread.start do
109
+ hi.update_ping if requirement[:ping]
110
+ hi.update_cwd if requirement[:cwd]
111
+ hi.update_ps if requirement[:ps]
112
+ hi.update_cpuinfo if requirement[:cpuinfo]
113
+ hi.update_meminfo if requirement[:meminfo]
114
+ end
115
+ end
116
+ threads.each do |hi, thread|
117
+ print "Waiting for #{hi.hostname}...\n" unless OPTIONS[:quiet]
118
+ thread.join # wait until all processes are completed
119
+ end
120
+ end
121
+
122
+ ## Output
123
+ hostinspectors.each do |hi|
124
+ # oldest update
125
+ oldest_key, val = requirement.min_by {|key,val| hi.time_updated(key.to_s)}
126
+ hostname = hi.hostname
127
+ timeago = time_ago( hi.time_updated(oldest_key.to_s))
128
+ hosttime = sprintf("%-10s (%12s) ",
129
+ hi.hostname, time_ago( hi.time_updated(oldest_key.to_s)))
130
+ begin
131
+ case subcommand
132
+ when "ping"
133
+ if hi.fetch('ping')
134
+ hosttime += "o"
135
+ else
136
+ hosttime += "x"
137
+ end
138
+ puts hosttime
139
+ when "alive"
140
+ puts hostname if hi.fetch('ping')
141
+ when "cwd"
142
+ puts hosttime
143
+ hi.fetch('cwd').each do |pid, cwd|
144
+ printf(" %5s %s\n", pid, cwd)
145
+ end
146
+ when "ps"
147
+ puts hosttime
148
+ hi.fetch('ps').each do |pid, ps|
149
+ printf(" %-10s %5s\n", hi.hostname, pid)
150
+ end
151
+ when "cpuinfo"
152
+ result = hosttime
153
+ begin
154
+ cpuinfo = hi.fetch('cpuinfo')
155
+ result += sprintf("%2d_cores %s\n",
156
+ cpuinfo.size, cpuinfo[0]["model name"].gsub(/ +/, '_'))
157
+ rescue
158
+ result += sprintf("not_obtained\n")
159
+ end
160
+ puts result
161
+ when "meminfo"
162
+ result = hosttime
163
+ meminfo = hi.fetch('meminfo')
164
+ result += sprintf("%s\n", meminfo['MemTotal'])
165
+ puts result
166
+ when "load"
167
+ result = hosttime
168
+ ps = hi.fetch("ps")
169
+ cwd = hi.fetch("cwd")
170
+ pid, process = ps.max_by {|key, val| val['cpu'].to_f}
171
+ command = File.basename(process['command'].split[0])
172
+ result += sprintf("%5s %8s %5.1f %5.1f %20s %s\n",
173
+ pid, process['user'], process['cpu'], process['mem'],
174
+ command, cwd[pid])
175
+ puts result
176
+ end
177
+ rescue Comana::HostInspector::NoUpdateFile
178
+ puts hosttime + "------------"
179
+ rescue NoMethodError # 主にデータを取れなかった時用
180
+ puts hosttime + "------------"
181
+ end
182
+ end
data/bin/machinestatus CHANGED
@@ -1,6 +1,10 @@
1
1
  #! /usr/bin/env ruby
2
2
  # coding: utf-8
3
3
 
4
+
5
+ puts "machinestatus is obsoleted. use 'hostinfo'"
6
+ exit
7
+
4
8
  USAGE = <<HERE
5
9
  machinestatus HostA HostB
6
10
  machinestatus -g GroupA GroupB #-g option interprets as group name
@@ -57,8 +61,6 @@ else
57
61
  end
58
62
  hosts = hs.select_all if ARGV.empty?
59
63
 
60
- #pp hosts;exit
61
-
62
64
  ##Collect information
63
65
  Thread.abort_on_exception = true
64
66
  results = {}
@@ -186,4 +188,5 @@ end
186
188
 
187
189
  data = data.unshift(titles)
188
190
  #pp data
189
- Tefil::ColumnFormer.form(data, $stdout, "|")
191
+ t = Tefil::ColumnFormer.new
192
+ t.form(data, $stdout, "|")
data/bin/queueinfo ADDED
@@ -0,0 +1,28 @@
1
+ #! /usr/bin/env ruby
2
+
3
+
4
+ require 'thor'
5
+ require 'comana'
6
+ require 'pp'
7
+
8
+ ### Command template
9
+ class Queueinfo < Thor
10
+ desc "light", "Detect the lightest queue."
11
+
12
+ def lightqueue
13
+ qinfo = Comana::QueueManager.new
14
+ qinfo.light_queue
15
+ end
16
+
17
+ #def job
18
+ #end
19
+
20
+ def show
21
+
22
+ end
23
+
24
+ end
25
+
26
+ Queueinfo.start(ARGV)
27
+
28
+
data/comana.gemspec ADDED
@@ -0,0 +1,110 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: comana 0.1.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "comana"
9
+ s.version = "0.1.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["ippei94da"]
14
+ s.date = "2016-03-04"
15
+ s.description = "Comana, COmputation MANAger,\n is a software to provide a framework of\n managing scientific computing.\n Researchers on computing have to check calculation and\n generate new calculation and execute, repeatedly.\n The abstract class that this gem provide would help the work.\n "
16
+ s.email = "ippei94da@gmail.com"
17
+ s.executables = ["genqsub", "hostinfo", "machinestatus", "queueinfo", "scpall", "sshall"]
18
+ s.extra_rdoc_files = [
19
+ "LICENSE.txt",
20
+ "README.rdoc"
21
+ ]
22
+ s.files = [
23
+ ".document",
24
+ ".rspec",
25
+ "CHANGES",
26
+ "Gemfile",
27
+ "LICENSE.txt",
28
+ "README.rdoc",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "bin/genqsub",
32
+ "bin/hostinfo",
33
+ "bin/machinestatus",
34
+ "bin/queueinfo",
35
+ "bin/scpall",
36
+ "bin/sshall",
37
+ "comana.gemspec",
38
+ "example/dot.clustersetting",
39
+ "lib/comana.rb",
40
+ "lib/comana/clustersetting.rb",
41
+ "lib/comana/computationmanager.rb",
42
+ "lib/comana/gridenginescript.rb",
43
+ "lib/comana/hostinspector.rb",
44
+ "lib/comana/hostselector.rb",
45
+ "lib/comana/queuemanager.rb",
46
+ "lib/comana/queuesubmitter.rb",
47
+ "memo.txt",
48
+ "test/helper.rb",
49
+ "test/hostinspector/.gitignore",
50
+ "test/locked/input_a",
51
+ "test/locked/input_b",
52
+ "test/locked/lock_comana/dummy",
53
+ "test/locked_outputted/input_a",
54
+ "test/locked_outputted/input_b",
55
+ "test/locked_outputted/lock_comana/dummy",
56
+ "test/locked_outputted/output",
57
+ "test/not_executable/dummy",
58
+ "test/not_started/input_a",
59
+ "test/not_started/input_b",
60
+ "test/outputted/input_a",
61
+ "test/outputted/input_b",
62
+ "test/outputted/output",
63
+ "test/pbsnodes/Br09.xml",
64
+ "test/pbsnodes/Br10.xml",
65
+ "test/pbsnodes/Ge00.xml",
66
+ "test/pbsnodes/Ge08.xml",
67
+ "test/pbsnodes/all.xml",
68
+ "test/queuesubmitter/locked/lock_queuesubmitter/dummy",
69
+ "test/queuesubmitter/unlocked/dummy",
70
+ "test/test_clustersetting.rb",
71
+ "test/test_computationmanager.rb",
72
+ "test/test_gridenginescript.rb",
73
+ "test/test_hostinspector.rb",
74
+ "test/test_hostselector.rb",
75
+ "test/test_queuemanager.rb",
76
+ "test/test_queuesubmitter.rb"
77
+ ]
78
+ s.homepage = "http://github.com/ippei94da/comana"
79
+ s.licenses = ["MIT"]
80
+ s.rubygems_version = "2.2.3"
81
+ s.summary = "Manager for scientific computing"
82
+
83
+ if s.respond_to? :specification_version then
84
+ s.specification_version = 4
85
+
86
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
87
+ s.add_development_dependency(%q<test-unit>, ["~> 3.1"])
88
+ s.add_development_dependency(%q<rdoc>, ["~> 4.2"])
89
+ s.add_development_dependency(%q<bundler>, ["~> 1.11"])
90
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
91
+ s.add_development_dependency(%q<simplecov>, ["~> 0.11"])
92
+ s.add_development_dependency(%q<tefil>, [">= 0.1"])
93
+ else
94
+ s.add_dependency(%q<test-unit>, ["~> 3.1"])
95
+ s.add_dependency(%q<rdoc>, ["~> 4.2"])
96
+ s.add_dependency(%q<bundler>, ["~> 1.11"])
97
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
98
+ s.add_dependency(%q<simplecov>, ["~> 0.11"])
99
+ s.add_dependency(%q<tefil>, [">= 0.1"])
100
+ end
101
+ else
102
+ s.add_dependency(%q<test-unit>, ["~> 3.1"])
103
+ s.add_dependency(%q<rdoc>, ["~> 4.2"])
104
+ s.add_dependency(%q<bundler>, ["~> 1.11"])
105
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
106
+ s.add_dependency(%q<simplecov>, ["~> 0.11"])
107
+ s.add_dependency(%q<tefil>, [">= 0.1"])
108
+ end
109
+ end
110
+
@@ -8,21 +8,22 @@ require "yaml"
8
8
  # E.g.,
9
9
  # "Fe", "Fe00", "Fe01" are of series "Fe" and not "F"
10
10
  class Comana::ClusterSetting
11
- attr_reader :groups, :pbs_server
11
+ attr_reader :data_file, :groups, :pbs_server
12
12
 
13
13
  DEFAULT_DATA_FILE = ENV["HOME"] + "/.clustersetting"
14
14
 
15
15
  class NoEntryError < Exception; end
16
16
 
17
17
  #
18
- def initialize(settings)
18
+ def initialize(settings, data_file = nil)
19
19
  @pbs_server = settings["pbs_server"]
20
20
  @groups = settings["groups"]
21
+ @data_file = data_file
21
22
  end
22
23
 
23
24
  def self.load_file(data_file = DEFAULT_DATA_FILE)
24
25
  settings = YAML.load_file(data_file)
25
- self.new settings
26
+ self.new(settings, data_file)
26
27
  end
27
28
 
28
29
  #Return belonged cluster of the host.
@@ -1,7 +1,7 @@
1
1
  #! /usr/bin/env ruby
2
2
  # coding: utf-8
3
3
 
4
- # This class profides a framework of scientific computation.
4
+ # This class provides a framework of scientific computation.
5
5
  # Users have to redefine some methods in subclasses for various computation.
6
6
  #
7
7
  class Comana::ComputationManager
@@ -18,6 +18,28 @@ class Comana::ComputationManager
18
18
  @alive_time = 3600
19
19
  end
20
20
 
21
+ def self.exec(args)
22
+ targets = args
23
+ targets = [ENV['PWD']] if targets.size == 0
24
+
25
+ targets.each do |dir|
26
+ print "#{dir}..."
27
+ begin
28
+ calc_dir = self.new(dir)
29
+ rescue => exc
30
+ puts "Not suitable directory, due of an exception: #{exc}"
31
+ next
32
+ end
33
+
34
+ begin
35
+ calc_dir.start
36
+ rescue Comana::ComputationManager::AlreadyStartedError
37
+ puts "Already started."
38
+ next
39
+ end
40
+ end
41
+ end
42
+
21
43
  # Return a symbol which indicate state of calculation.
22
44
  # :yet not started
23
45
  # :started started, but not ended, including short time from last output
@@ -33,7 +55,7 @@ class Comana::ComputationManager
33
55
  # Execute calculation.
34
56
  # If log of ComputationManager exist, raise ComputationManager::AlreadyStartedError,
35
57
  # because the calculation has been done by other process already.
36
- def start
58
+ def exec
37
59
  begin
38
60
  Dir.mkdir "#{@dir}/#{@lockdir}"
39
61
  rescue Errno::EEXIST
@@ -46,6 +68,7 @@ class Comana::ComputationManager
46
68
  prepare_next
47
69
  end
48
70
  end
71
+ alias start exec
49
72
 
50
73
  # Return latest modified time of files in calc dir recursively.
51
74
  # require "find"
@@ -0,0 +1,68 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ #
5
+ #
6
+ #
7
+ class GridEngineScript
8
+ #DEFAULT_QSUB_FILE = 'qsub.sh'
9
+ QSUB_PATH = '/usr/bin/qsub'
10
+
11
+ #
12
+ def initialize()
13
+ end
14
+
15
+ # series: name of computers' series
16
+ # ppn: number of using cores
17
+ def self.generate(io, series, ppn, command)
18
+ io.puts self.string(series, ppn, command)
19
+ end
20
+
21
+ def self.string(series, ppn, command)
22
+ string = <<HERE
23
+ \#! /bin/sh
24
+ \#$ -S /bin/sh
25
+ \#$ -cwd
26
+ \#$ -o stdout
27
+ \#$ -e stderr
28
+ \#$ -q #{series}.q
29
+ \#$ -pe #{series}.openmpi #{ppn}
30
+
31
+ MACHINE_FILE="machines"
32
+ ENV_FILE="printenv.log"
33
+
34
+ LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/opt/intel/mkl/lib/intel64:/opt/intel/lib/intel64:/opt/intel/lib:/opt/openmpi-intel/lib
35
+ export LD_LIBRARY_PATH
36
+
37
+ cd $SGE_O_WORKDIR
38
+ if [ -e $ENV_FILE ]; then
39
+ echo "$ENV_FILE already exist. Exit."
40
+ exit
41
+ fi
42
+ printenv | sort > printenv.log
43
+ cut -d " " -f 1,2 $PE_HOSTFILE | sed 's/ / cpu=/' > $MACHINE_FILE
44
+
45
+ #{command}
46
+ HERE
47
+ string
48
+ end
49
+
50
+ def self.write(series, ppn, command, io)
51
+ io.puts self.string(series, ppn, command)
52
+ end
53
+
54
+ def self.write_submit(series, ppn, command, filename)
55
+ #qsub_file = DEFAULT_QSUB_FILE
56
+ io = File.open(filename, 'w')
57
+ self.write(series, ppn, command, io)
58
+ io.close
59
+
60
+ logfile = filename.sub(/#{File.extname filename}$/, '.log')
61
+ command = "#{QSUB_PATH} #{filename} > #{logfile}"
62
+ #command = "qsub #{filename}"
63
+ puts command
64
+ system command
65
+ end
66
+
67
+ end
68
+