server_scripts 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/README.md +75 -0
- data/Rakefile +12 -0
- data/lib/server_scripts/computer/abci.rb +21 -0
- data/lib/server_scripts/computer/base.rb +30 -0
- data/lib/server_scripts/computer/sameer_pc.rb +13 -0
- data/lib/server_scripts/computer/tsubame.rb +36 -0
- data/lib/server_scripts/computer.rb +5 -0
- data/lib/server_scripts/executor/base.rb +10 -0
- data/lib/server_scripts/executor/mpi_program.rb +54 -0
- data/lib/server_scripts/executor/valgrind.rb +7 -0
- data/lib/server_scripts/executor/vanilla.rb +7 -0
- data/lib/server_scripts/executor.rb +4 -0
- data/lib/server_scripts/node_type.rb +6 -0
- data/lib/server_scripts/version.rb +3 -0
- data/lib/server_scripts.rb +136 -0
- data/server_scripts.gemspec +41 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b272c99b33f7ecb954965b83b6a465c349721df
|
4
|
+
data.tar.gz: 8d9d49ee51086a521c8144f935348302bf13cfb7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7fe5a9cc5b3ad76c397a9dbb0d7223f40b7e679fd0203749e4aeae7cc4e9194129ed8cdabf5117538ba75075a292dd9a8d3d090700e8eded4f196c10779bc7da
|
7
|
+
data.tar.gz: e8ee6d52ccaf1c75c4404d656437bf8e32d1053cdb93945bd067af2b5bbcf584ea5cc61e50caf6312c7d91a35209f20473ebaad5b928aa3d31fedf3e993a712f
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# server-scripts
|
2
|
+
|
3
|
+
A gem providing easily usable server scripts for various supercomputers and servers.
|
4
|
+
The following functionality is provided:
|
5
|
+
|
6
|
+
* Generate job scripts and run batch jobs on TSUBAME 3.0, ABCI and reedbush machines.
|
7
|
+
* Parse various kinds of profiling files and generate meaningful output.
|
8
|
+
|
9
|
+
# Usage
|
10
|
+
|
11
|
+
## ENV variables
|
12
|
+
|
13
|
+
Make sure the `SYSTEM` variable is set on your machine so that the gem will automatically
|
14
|
+
select the appropriate commands to run.
|
15
|
+
|
16
|
+
## Writing job scripts
|
17
|
+
|
18
|
+
### Simple openMPI job script
|
19
|
+
|
20
|
+
Use the `ServerScripts::BatchJob` class in your Ruby for outputting and submitting
|
21
|
+
job files. A simple MPI job can be generated and submitted as follows:
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
require 'server_scripts'
|
25
|
+
|
26
|
+
include ServerScripts
|
27
|
+
|
28
|
+
task = BatchJob.new do |t|
|
29
|
+
t.nodes = 4
|
30
|
+
t.npernode = 4
|
31
|
+
t.wall_time = "1:30:00"
|
32
|
+
t.out_file = "out.log"
|
33
|
+
t.err_file = "err.log"
|
34
|
+
t.node_type = NodeType::FULL
|
35
|
+
t.mpi = OPENMPI
|
36
|
+
t.set_env "STARPU_SCHED", "dmda"
|
37
|
+
t.set_env "MKL_NUM_THREADS", "1"
|
38
|
+
t.executable = "a.out"
|
39
|
+
t.options = "3 32768 2048 2 2"
|
40
|
+
end
|
41
|
+
|
42
|
+
task.submit!
|
43
|
+
```
|
44
|
+
This will generate a unique file name and submit it using the system's batch
|
45
|
+
job submission command.
|
46
|
+
|
47
|
+
### Intel MPI profiling job script
|
48
|
+
|
49
|
+
If you want to generate traces using intel MPI, you can use additional options
|
50
|
+
like setting the ITAC and VTUNE output file/folder names.
|
51
|
+
|
52
|
+
## Parse intel ITAC output
|
53
|
+
|
54
|
+
The intel ITAC tool can be helpful for generating traces of parallel MPI programs.
|
55
|
+
This class can be used for converting an ITAC file to an ideal trace and then generating
|
56
|
+
the function profile for obtaining things like the MPI wait time.
|
57
|
+
|
58
|
+
For extracting the MPI wait time from an ITAC trace, do the following:
|
59
|
+
``` ruby
|
60
|
+
require 'server_scripts'
|
61
|
+
|
62
|
+
itac = ServerScripts::Parser::ITAC.new("itac_file.stf")
|
63
|
+
itac.generate_ideal_trace!
|
64
|
+
|
65
|
+
# All times are reported in seconds.
|
66
|
+
|
67
|
+
puts itac.mpi_time(kind: :ideal)
|
68
|
+
puts itac.mpi_time(kind: :real)
|
69
|
+
puts itac.event_time("getrf_start", how: :total, kind: :real)
|
70
|
+
puts itac.event_time("getrf_start", how: :per_proc, kind: :real)
|
71
|
+
```
|
72
|
+
|
73
|
+
## Parse starpu worker info
|
74
|
+
|
75
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'server_scripts/version.rb'
|
5
|
+
|
6
|
+
gemspec = eval(IO.read("server_scripts.gemspec"))
|
7
|
+
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.libs << "lib"
|
11
|
+
t.test_files = FileList['test/**/test_*.rb']
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ServerScripts
|
2
|
+
module Computer
|
3
|
+
class ABCI < Base
|
4
|
+
FULL_NODE = "rt_F"
|
5
|
+
|
6
|
+
def header(node_type: FULL_NODE)
|
7
|
+
h = %q{
|
8
|
+
#!/bin/bash
|
9
|
+
|
10
|
+
#$ -cwd
|
11
|
+
#$ -l %{node_type}=%{nodes}
|
12
|
+
#$ -l h_rt=%{walltime}
|
13
|
+
#$ -N %{job_name}
|
14
|
+
#$ -o %{out_file}.log
|
15
|
+
#$ -e %{err_file}.log
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ServerScripts
|
2
|
+
module Computer
|
3
|
+
class Base
|
4
|
+
def initialize(node_type, nodes, job_name, wall_time, out_file,
|
5
|
+
err_file, env)
|
6
|
+
@node_type = node_type
|
7
|
+
@nodes = nodes
|
8
|
+
@job_name = job_name
|
9
|
+
@wall_time = wall_time
|
10
|
+
@out_file = out_file
|
11
|
+
@err_file = err_file
|
12
|
+
@env = env
|
13
|
+
end
|
14
|
+
|
15
|
+
def node_type
|
16
|
+
if @node_type == NodeType::FULL
|
17
|
+
self.class::FULL_NODE
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def env_setter
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def job_submit_cmd
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ServerScripts
|
2
|
+
module Computer
|
3
|
+
class TSUBAME < Base
|
4
|
+
HEADER = %q{#!/bin/bash
|
5
|
+
|
6
|
+
#$ -cwd
|
7
|
+
#$ -l %{node_type}=%{nodes}
|
8
|
+
#$ -l h_rt=%{wall_time}
|
9
|
+
#$ -N %{job_name}
|
10
|
+
#$ -o %{out_file}.log
|
11
|
+
#$ -e %{err_file}.log
|
12
|
+
}
|
13
|
+
|
14
|
+
FULL_NODE = "f_node"
|
15
|
+
|
16
|
+
def header
|
17
|
+
HEADER % {node_type: node_type, nodes: @nodes, wall_time: @wall_time,
|
18
|
+
job_name: @job_name, out_file: @out_file, err_file: @err_file}
|
19
|
+
end
|
20
|
+
|
21
|
+
def env_setter
|
22
|
+
str = "\n"
|
23
|
+
@env.each do |var, value|
|
24
|
+
str += "export #{var}=#{value}\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
str
|
28
|
+
end
|
29
|
+
|
30
|
+
def job_submit_cmd res_id: nil, batch_script:
|
31
|
+
res = res_id ? " -ar #{res_id} " : ""
|
32
|
+
"qsub -g #{ServeScripts.group_name} #{res} #{batch_script}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module ServerScripts
|
3
|
+
module Executor
|
4
|
+
class MPIProgram < Base
|
5
|
+
attr_reader :npernode
|
6
|
+
attr_reader :nprocs
|
7
|
+
attr_reader :env
|
8
|
+
|
9
|
+
def initialize(npernode: , nprocs:, env: {})
|
10
|
+
@npernode = npernode
|
11
|
+
@nprocs = nprocs
|
12
|
+
@env = env
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class OpenMPI < MPIProgram
|
17
|
+
def run_cmd
|
18
|
+
"mpirun --mca mpi_cuda_support 0 #{env_variables} -N #{@npernode} -np #{@nprocs}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def env_variables
|
24
|
+
str = ""
|
25
|
+
@env.each_key do |k|
|
26
|
+
str += " -x #{k} "
|
27
|
+
end
|
28
|
+
|
29
|
+
str
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class IntelMPI < MPIProgram
|
34
|
+
attr_accessor :enable_itac
|
35
|
+
attr_accessor :vtune_fname
|
36
|
+
|
37
|
+
def initialize(*args)
|
38
|
+
super
|
39
|
+
@vtune_fname = "DEFAULT_VTUNE"
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_cmd
|
43
|
+
hydra = @enable_itac ? "mpiexec.hydra -trace" : "mpiexec.hydra"
|
44
|
+
cmd = "#{hydra} -ppn #{@npernode} -np #{@nprocs} "
|
45
|
+
if @enable_itac
|
46
|
+
cmd += "amplxe-cl -trace-mpi –collect hpc-performance –r #{@vtune_fname} "
|
47
|
+
end
|
48
|
+
|
49
|
+
cmd
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'server_scripts/node_type'
|
3
|
+
require 'server_scripts/executor'
|
4
|
+
require 'server_scripts/computer'
|
5
|
+
require 'server_scripts/version'
|
6
|
+
|
7
|
+
module ServerScripts
|
8
|
+
class << self
|
9
|
+
def system
|
10
|
+
sys = ENV["SYSTEM"]
|
11
|
+
|
12
|
+
if sys == "SAMEER-PC"
|
13
|
+
ServerScripts::Computer::SameerPC
|
14
|
+
elsif sys == "TSUBAME"
|
15
|
+
ServerScripts::Computer::TSUBAME
|
16
|
+
elsif sys == "ABCI"
|
17
|
+
ServerScripts::Computer::ABCI
|
18
|
+
elsif sys == "REEDBUSH"
|
19
|
+
ServerScripts::Computer::REEDBUSH
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def group_name
|
24
|
+
ENV["GROUP_NAME"]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class BatchJob
|
29
|
+
attr_accessor :job_name
|
30
|
+
attr_accessor :out_file
|
31
|
+
attr_accessor :err_file
|
32
|
+
attr_accessor :wall_time
|
33
|
+
attr_accessor :node_type
|
34
|
+
attr_accessor :options
|
35
|
+
attr_accessor :nodes
|
36
|
+
attr_accessor :npernode
|
37
|
+
attr_accessor :nprocs
|
38
|
+
attr_accessor :run_cmd, :executor, :executable
|
39
|
+
attr_accessor :additional_commands
|
40
|
+
attr_accessor :enable_intel_itac
|
41
|
+
attr_accessor :intel_vtune_fname
|
42
|
+
|
43
|
+
attr_reader :env
|
44
|
+
attr_reader :job_fname
|
45
|
+
attr_reader :system
|
46
|
+
|
47
|
+
def initialize job_fname
|
48
|
+
@job_fname = "sample_job_script.sh"
|
49
|
+
@job_name = "sample"
|
50
|
+
@out_file = "sample_out"
|
51
|
+
@err_file = "sample_err"
|
52
|
+
@wall_time = "1:00:00"
|
53
|
+
@node_type = NodeType::FULL
|
54
|
+
@nodes = 1
|
55
|
+
@npernode = 1
|
56
|
+
@nprocs = nil
|
57
|
+
@run_cmd = nil
|
58
|
+
@executor = :vanilla
|
59
|
+
@env = {}
|
60
|
+
@executable = "./a.out"
|
61
|
+
@job_script = ""
|
62
|
+
@enable_intel_itac = false
|
63
|
+
@additional_commands = []
|
64
|
+
|
65
|
+
yield self
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_env var, value
|
69
|
+
raise ArgumentError, "Env #{var} is already set to #{value}." if @env[var]
|
70
|
+
@env[var] = value
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate_job_script!
|
74
|
+
@system = ServerScripts.system.new(@node_type, @nodes, @job_name,
|
75
|
+
@wall_time, @out_file, @err_file, @env)
|
76
|
+
configure_executor!
|
77
|
+
|
78
|
+
@job_script += @system.header
|
79
|
+
@job_script += @system.env_setter
|
80
|
+
@additional_commands.each do |c|
|
81
|
+
@job_script += c + "\n"
|
82
|
+
end
|
83
|
+
@job_script += "#{@executor.run_cmd} #{@executable} #{@options}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def submit!
|
87
|
+
generate_job_script! unless @job_script
|
88
|
+
File.write(@job_fname, @job_script)
|
89
|
+
# system(@system.job_submit_cmd(batch_script: @job_fname))
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def configure_executor!
|
95
|
+
check_process_counts
|
96
|
+
@nprocs = @npernode * @nodes
|
97
|
+
|
98
|
+
if @executor == :openmpi
|
99
|
+
@executor = Executor::OpenMPI.new(npernode: @npernode, nprocs: @nprocs, env: @env)
|
100
|
+
elsif @executor == :intel
|
101
|
+
@executor = Executor::IntelMPI.new(npernode: @npernode, nprocs: @nprocs, env: @env)
|
102
|
+
@executor.enable_itac = !!@enable_intel_itac
|
103
|
+
@executor.vtune_fname = @intel_vtune_fname
|
104
|
+
elsif @executor == :vanilla
|
105
|
+
@executor = Executor::Vanilla.new
|
106
|
+
else
|
107
|
+
raise ArgumentError, "Cannot find MPI implementation #{@executor}."
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_process_counts
|
112
|
+
if @nprocs && @nprocs != @npernode * @nodes
|
113
|
+
raise ArgumentError, "Number of processes should be #{@npernode * @nodes} not #{@nprocs}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Parser
|
119
|
+
class ITAC
|
120
|
+
attr_reader :ideal_trace_file, :real_trace_file
|
121
|
+
|
122
|
+
def initialize itac_file
|
123
|
+
@itac_file = itac_file
|
124
|
+
@ideal_trace_file = nil
|
125
|
+
@real_trace_file = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
def generate_ideal_trace!
|
129
|
+
|
130
|
+
end
|
131
|
+
end # class ITAC
|
132
|
+
end # module Parser
|
133
|
+
end # module ServerScripts
|
134
|
+
|
135
|
+
|
136
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#coding: utf-8
|
2
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require 'server_scripts/version.rb'
|
5
|
+
|
6
|
+
def self.get_files
|
7
|
+
files = []
|
8
|
+
['ext', 'lib', 'spec'].each do |folder|
|
9
|
+
files.concat Dir.glob "#{folder}/**/*"
|
10
|
+
end
|
11
|
+
|
12
|
+
files.concat(
|
13
|
+
["Gemfile", "server_scripts.gemspec", "README.md", "Rakefile"])
|
14
|
+
|
15
|
+
files
|
16
|
+
end
|
17
|
+
files = get_files
|
18
|
+
|
19
|
+
ServerScripts::DESCRIPTION = <<MSG
|
20
|
+
Easily write scripts for submitted jobs to various machines.
|
21
|
+
MSG
|
22
|
+
|
23
|
+
Gem::Specification.new do |spec|
|
24
|
+
spec.name = 'server_scripts'
|
25
|
+
spec.version = ServerScripts::VERSION
|
26
|
+
spec.authors = ['Sameer Deshmukh']
|
27
|
+
spec.email = ['sameer.deshmukh93@gmail.com']
|
28
|
+
spec.summary = %q{Easily write scripts for submitted jobs to various machines.}
|
29
|
+
spec.description = ServerScripts::DESCRIPTION
|
30
|
+
spec.homepage = "https://github.com/v0dro/server-scripts"
|
31
|
+
spec.license = 'BSD-3 Clause'
|
32
|
+
|
33
|
+
spec.files = files
|
34
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
35
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
|
38
|
+
spec.add_development_dependency 'minitest', '~> 5.11'
|
39
|
+
spec.add_development_dependency 'minitest-hooks'
|
40
|
+
spec.add_development_dependency 'minitest-fail-fast'
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: server_scripts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sameer Deshmukh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-hooks
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-fail-fast
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: 'Easily write scripts for submitted jobs to various machines.
|
56
|
+
|
57
|
+
'
|
58
|
+
email:
|
59
|
+
- sameer.deshmukh93@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- Gemfile
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/server_scripts.rb
|
68
|
+
- lib/server_scripts/computer.rb
|
69
|
+
- lib/server_scripts/computer/abci.rb
|
70
|
+
- lib/server_scripts/computer/base.rb
|
71
|
+
- lib/server_scripts/computer/sameer_pc.rb
|
72
|
+
- lib/server_scripts/computer/tsubame.rb
|
73
|
+
- lib/server_scripts/executor.rb
|
74
|
+
- lib/server_scripts/executor/base.rb
|
75
|
+
- lib/server_scripts/executor/mpi_program.rb
|
76
|
+
- lib/server_scripts/executor/valgrind.rb
|
77
|
+
- lib/server_scripts/executor/vanilla.rb
|
78
|
+
- lib/server_scripts/node_type.rb
|
79
|
+
- lib/server_scripts/version.rb
|
80
|
+
- server_scripts.gemspec
|
81
|
+
homepage: https://github.com/v0dro/server-scripts
|
82
|
+
licenses:
|
83
|
+
- BSD-3 Clause
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.6.14
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Easily write scripts for submitted jobs to various machines.
|
105
|
+
test_files: []
|