server_scripts 0.0.1
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.
- 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: []
|