ood_core 0.0.4 → 0.0.5
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 +4 -4
- data/CHANGELOG.md +63 -20
- data/lib/ood_core.rb +6 -0
- data/lib/ood_core/batch_connect/factory.rb +42 -0
- data/lib/ood_core/batch_connect/template.rb +207 -0
- data/lib/ood_core/batch_connect/templates/basic.rb +23 -0
- data/lib/ood_core/batch_connect/templates/vnc.rb +201 -0
- data/lib/ood_core/cluster.rb +33 -8
- data/lib/ood_core/errors.rb +6 -0
- data/lib/ood_core/job/adapter.rb +11 -0
- data/lib/ood_core/job/adapters/lsf.rb +16 -22
- data/lib/ood_core/job/adapters/lsf/batch.rb +28 -15
- data/lib/ood_core/job/adapters/lsf/helper.rb +79 -0
- data/lib/ood_core/job/adapters/pbspro.rb +424 -0
- data/lib/ood_core/job/adapters/slurm.rb +8 -0
- data/lib/ood_core/job/adapters/torque.rb +32 -2
- data/lib/ood_core/job/info.rb +9 -2
- data/lib/ood_core/refinements/hash_extensions.rb +9 -0
- data/lib/ood_core/version.rb +1 -1
- data/ood_core.gemspec +1 -1
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bab7efc85cb79ee4fbec84bd148968b5e3e7efa4
|
4
|
+
data.tar.gz: 856dbe1ae2f138f409875b6b19e007129d4ece73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d9b87b7134e3e72fff1191ac24658a5243d71cee4d1942a1f5a946345640b4f6a999a7b662db9924c137234183cbef5eafbd355bbb1fe1ee0249a91b32c3866
|
7
|
+
data.tar.gz: a958afc1560a75e358f9655df0342a9831e49a2a4d3658bf74878fc61ae7a135a34248cd7b974dd5bea4190129481bb7f2810bf24a38d01fb12c6a64a74a9641
|
data/CHANGELOG.md
CHANGED
@@ -1,33 +1,76 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
2
|
|
3
|
-
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
-
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
6
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
7
|
|
7
|
-
|
8
|
-
across resource managers
|
9
|
-
- removed `OodCore::Job::Script#join_files` due to lack of support in
|
10
|
-
resource managers
|
11
|
-
- by default all PBS jobs output stdout & stderr to output path unless an
|
12
|
-
error path is specified (mimics behavior of Slurm and LSF)
|
8
|
+
## [Unreleased]
|
13
9
|
|
14
|
-
## 0.0.
|
10
|
+
## [0.0.5] - 2017-07-05
|
15
11
|
|
16
|
-
|
12
|
+
### Added
|
17
13
|
|
18
|
-
|
14
|
+
- Add wallclock time limit to `OodCore::Job::Info` object.
|
15
|
+
- Add further support for the LSF adapter.
|
16
|
+
- Add a new Batch Connect template feature that builds batch scripts to launch
|
17
|
+
web servers.
|
18
|
+
- Add support for the PBS Professional resource manager.
|
19
|
+
- Add method to filter list of batch jobs for a given owner or owners.
|
19
20
|
|
20
|
-
|
21
|
+
### Changed
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
- Torque adapter provides nodes/procs info if available for non-running jobs.
|
24
|
+
- Slurm adapter provides node info if available for non-running jobs.
|
25
|
+
- Changed the `CHANGELOG.md` formatting.
|
24
26
|
|
25
|
-
|
27
|
+
### Removed
|
26
28
|
|
27
|
-
|
29
|
+
- Remove deprecated tests for the Slurm adapter.
|
28
30
|
|
29
|
-
|
31
|
+
### Fixed
|
30
32
|
|
31
|
-
|
33
|
+
- Fix parsing bjobs output for LSF 9.1, which has extra SLOTS column.
|
32
34
|
|
33
|
-
|
35
|
+
## [0.0.4] - 2017-05-17
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
|
39
|
+
- By default all PBS jobs output stdout & stderr to output path unless an error
|
40
|
+
path is specified (mimics behavior of Slurm and LSF)
|
41
|
+
|
42
|
+
### Removed
|
43
|
+
|
44
|
+
- Remove `OodCore::Job::Script#min_phys_memory` due to lack of commonality
|
45
|
+
across resource managers.
|
46
|
+
- Remove `OodCore::Job::Script#join_files` due to lack of support in resource
|
47
|
+
managers.
|
48
|
+
|
49
|
+
## [0.0.3] - 2017-04-28
|
50
|
+
|
51
|
+
### Added
|
52
|
+
|
53
|
+
- Provide support for Slurm conf file.
|
54
|
+
|
55
|
+
### Fixed
|
56
|
+
|
57
|
+
- Correct code documentation for `Script#min_phys_memory`.
|
58
|
+
- Add fix for login feature being allowed on all clusters even if not defined.
|
59
|
+
|
60
|
+
## [0.0.2] - 2017-04-27
|
61
|
+
|
62
|
+
### Removed
|
63
|
+
|
64
|
+
- Remove the `OodCore::Job::NodeRequest` object.
|
65
|
+
|
66
|
+
## 0.0.1 - 2017-04-17
|
67
|
+
|
68
|
+
### Added
|
69
|
+
|
70
|
+
- Initial release!
|
71
|
+
|
72
|
+
[Unreleased]: https://github.com/OSC/ood_core/compare/v0.0.5...HEAD
|
73
|
+
[0.0.5]: https://github.com/OSC/ood_core/compare/v0.0.4...v0.0.5
|
74
|
+
[0.0.4]: https://github.com/OSC/ood_core/compare/v0.0.3...v0.0.4
|
75
|
+
[0.0.3]: https://github.com/OSC/ood_core/compare/v0.0.2...v0.0.3
|
76
|
+
[0.0.2]: https://github.com/OSC/ood_core/compare/v0.0.1...v0.0.2
|
data/lib/ood_core.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "ood_core/refinements/hash_extensions"
|
2
|
+
|
3
|
+
module OodCore
|
4
|
+
module BatchConnect
|
5
|
+
# A factory that builds a batch connect template object from a
|
6
|
+
# configuration.
|
7
|
+
class Factory
|
8
|
+
using Refinements::HashExtensions
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Build a batch connect template from a configuration
|
12
|
+
# @param config [#to_h] configuration describing batch connect template
|
13
|
+
# @option config [#to_s] :template The batch connect template to use
|
14
|
+
# @raise [TemplateNotSpecified] if no template is specified
|
15
|
+
# @raise [TemplateNotFound] if the specified template does not exist
|
16
|
+
# @return [Template] the batch connect template object
|
17
|
+
def build(config)
|
18
|
+
c = config.to_h.symbolize_keys
|
19
|
+
|
20
|
+
template = c.fetch(:template) { raise TemplateNotSpecified, "batch connect configuration does not specify template" }.to_s
|
21
|
+
|
22
|
+
path_to_template = "ood_core/batch_connect/templates/#{template}"
|
23
|
+
begin
|
24
|
+
require path_to_template
|
25
|
+
rescue Gem::LoadError => e
|
26
|
+
raise Gem::LoadError, "Specified '#{template}' for batch connect template, but the gem is not loaded."
|
27
|
+
rescue LoadError => e
|
28
|
+
raise LoadError, "Could not load '#{template}'. Make sure that that batch connect template in the configuration file is valid."
|
29
|
+
end
|
30
|
+
|
31
|
+
template_method = "build_#{template}"
|
32
|
+
|
33
|
+
unless respond_to?(template_method)
|
34
|
+
raise TemplateNotFound, "batch connect configuration specifies nonexistent #{template} template"
|
35
|
+
end
|
36
|
+
|
37
|
+
send(template_method, c)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require "ood_core/refinements/hash_extensions"
|
2
|
+
|
3
|
+
module OodCore
|
4
|
+
module BatchConnect
|
5
|
+
# A template class that renders a batch script designed to facilitate
|
6
|
+
# external connections to the running job
|
7
|
+
class Template
|
8
|
+
using Refinements::HashExtensions
|
9
|
+
using Refinements::ArrayExtensions
|
10
|
+
|
11
|
+
# The context used to render this template
|
12
|
+
# @return [Hash] context hash
|
13
|
+
attr_reader :context
|
14
|
+
|
15
|
+
# @param context [#to_h] the context used to render the template
|
16
|
+
# @option context [#to_s] :work_dir Working directory for batch script
|
17
|
+
# @option context [#to_s] :conn_file ("connection.yml") The file that
|
18
|
+
# holds connection information
|
19
|
+
# @option context [#to_sym, Array<#to_sym>] :conn_params ([]) A list of
|
20
|
+
# connection parameters added to the connection file (`:host`, `:port`,
|
21
|
+
# and `:password` will always exist)
|
22
|
+
# @option context [#to_s] :bash_helpers ("...") Bash helper methods
|
23
|
+
# @option context [#to_i] :min_port (2000) Minimum port used when looking
|
24
|
+
# for available port
|
25
|
+
# @option context [#to_i] :max_port (65535) Maximum port used when
|
26
|
+
# looking for available port
|
27
|
+
# @option context [#to_i] :passwd_size (32) Length of randomly generated
|
28
|
+
# password
|
29
|
+
# @option context [#to_s] :script_wrapper ("%s") Bash code that wraps
|
30
|
+
# around the body of the template script (use `%s` to interpolate the
|
31
|
+
# body)
|
32
|
+
# @option context [#to_s] :before_script ("...") Bash code run before the
|
33
|
+
# main script is forked off
|
34
|
+
# @option context [#to_s] :before_file ("before.sh") Path to script that
|
35
|
+
# is sourced before main script is forked (assumes you don't modify
|
36
|
+
# `:before_script`)
|
37
|
+
# @option context [#to_s] :run_script ("...") Bash code that is forked
|
38
|
+
# off and treated as the main script
|
39
|
+
# @option context [#to_s] :script_file ("./script.sh") Path to script
|
40
|
+
# that is forked as the main scripta (assumes you don't modify
|
41
|
+
# `:run_script`)
|
42
|
+
# @option context [#to_s] :timeout ("") Timeout the main script in
|
43
|
+
# seconds, if empty then let script run for full walltime (assumes you
|
44
|
+
# don't modify `:run_script`)
|
45
|
+
# @option context [#to_s] :clean_script ("...") Bash code run during
|
46
|
+
# clean up after job finishes
|
47
|
+
# @option context [#to_s] :clean_file ("clean.sh") Path to script that is
|
48
|
+
# sourced during clean up (assumes you don't modify `:clean_script`)
|
49
|
+
def initialize(context = {})
|
50
|
+
@context = context.to_h.compact.symbolize_keys
|
51
|
+
raise ArgumentError, "No work_dir specified. Missing argument: work_dir" unless context.include?(:work_dir)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Render this template as string
|
55
|
+
# @return [String] rendered template
|
56
|
+
def to_s
|
57
|
+
<<-EOT.gsub(/^ {10}/, '')
|
58
|
+
#!/bin/bash
|
59
|
+
|
60
|
+
#{script_wrapper}
|
61
|
+
EOT
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
# Working directory that batch script runs in
|
66
|
+
def work_dir
|
67
|
+
context.fetch(:work_dir).to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
# The file that holds the connection information in yaml format
|
71
|
+
def conn_file
|
72
|
+
context.fetch(:conn_file, "connection.yml").to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
# The parameters to include in the connection file
|
76
|
+
def conn_params
|
77
|
+
conn_params = Array.wrap(context.fetch(:conn_params, [])).map(&:to_sym)
|
78
|
+
(conn_params + [:host, :port, :password]).uniq
|
79
|
+
end
|
80
|
+
|
81
|
+
# Helper methods used in the bash scripts
|
82
|
+
def bash_helpers
|
83
|
+
context.fetch(:bash_helpers) do
|
84
|
+
min_port = context.fetch(:min_port, 2000).to_i
|
85
|
+
max_port = context.fetch(:max_port, 65535).to_i
|
86
|
+
passwd_size = context.fetch(:passwd_size, 32).to_i
|
87
|
+
|
88
|
+
<<-EOT.gsub(/^ {14}/, '')
|
89
|
+
# Generate random integer in range [$1..$2]
|
90
|
+
function random () {
|
91
|
+
shuf -i ${1}-${2} -n 1
|
92
|
+
}
|
93
|
+
|
94
|
+
# Check if port $1 is in use
|
95
|
+
function used_port () {
|
96
|
+
local PORT=${1}
|
97
|
+
nc -z localhost ${PORT} &>/dev/null
|
98
|
+
}
|
99
|
+
|
100
|
+
# Find available port in range [$1..$2]
|
101
|
+
# Default: [#{min_port}..#{max_port}]
|
102
|
+
function find_port () {
|
103
|
+
local PORT=$(random ${1:-#{min_port}} ${2:-#{max_port}})
|
104
|
+
while $(used_port ${PORT}); do
|
105
|
+
PORT=$(random ${1:-#{min_port}} ${2:-#{max_port}})
|
106
|
+
done
|
107
|
+
echo ${PORT}
|
108
|
+
}
|
109
|
+
|
110
|
+
# Generate random alphanumeric password with $1 (default: #{passwd_size}) characters
|
111
|
+
function create_passwd () {
|
112
|
+
tr -cd '[:alnum:]' < /dev/urandom 2>/dev/null | head -c${1:-#{passwd_size}}
|
113
|
+
}
|
114
|
+
EOT
|
115
|
+
end.to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
# Bash code that wraps around the body of the template script (use `%s`
|
119
|
+
# to interpolate the body)
|
120
|
+
def script_wrapper
|
121
|
+
context.fetch(:script_wrapper, "%s").to_s % base_script
|
122
|
+
end
|
123
|
+
|
124
|
+
# Source in a developer defined script before running the main script
|
125
|
+
def before_script
|
126
|
+
context.fetch(:before_script) do
|
127
|
+
before_file = context.fetch(:before_file, "before.sh").to_s
|
128
|
+
|
129
|
+
"host=$(hostname)\n[[ -e \"#{before_file}\" ]] && source \"#{before_file}\""
|
130
|
+
end.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
# Fork off a developer defined main script and possibly time it out after
|
134
|
+
# a period of time
|
135
|
+
def run_script
|
136
|
+
context.fetch(:run_script) do
|
137
|
+
script_file = context.fetch(:script_file, "./script.sh").to_s
|
138
|
+
timeout = context.fetch(:timeout, "").to_s
|
139
|
+
|
140
|
+
timeout.empty? ? "\"#{script_file}\"" : "timeout #{timeout} \"#{script_file}\""
|
141
|
+
end.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
# Source in a developer defined script after running the main script
|
145
|
+
def after_script
|
146
|
+
context.fetch(:after_script) do
|
147
|
+
after_file = context.fetch(:after_file, "after.sh").to_s
|
148
|
+
|
149
|
+
"[[ -e \"#{after_file}\" ]] && source \"#{after_file}\""
|
150
|
+
end.to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
# Source in a developer defined clean up script that is run during the
|
154
|
+
# clean up stage
|
155
|
+
def clean_script
|
156
|
+
context.fetch(:clean_script) do
|
157
|
+
clean_file = context.fetch(:clean_file, "clean.sh").to_s
|
158
|
+
|
159
|
+
"[[ -e \"#{clean_file}\" ]] && source \"#{clean_file}\""
|
160
|
+
end.to_s
|
161
|
+
end
|
162
|
+
|
163
|
+
# The base script template
|
164
|
+
def base_script
|
165
|
+
<<-EOT.gsub(/^ {12}/, '')
|
166
|
+
cd #{work_dir}
|
167
|
+
|
168
|
+
# Generate a connection yaml file with given parameters
|
169
|
+
function create_yml () {
|
170
|
+
echo "Generating connection YAML file..."
|
171
|
+
(
|
172
|
+
umask 077
|
173
|
+
echo -e "#{conn_params.map { |p| "#{p}: $#{p}" }.join('\n')}" > "#{conn_file}"
|
174
|
+
)
|
175
|
+
}
|
176
|
+
|
177
|
+
# Cleanliness is next to Godliness
|
178
|
+
function clean_up () {
|
179
|
+
echo "Cleaning up..."
|
180
|
+
#{clean_script.gsub(/\n(?=[^\s])/, "\n ")}
|
181
|
+
pkill -P $$
|
182
|
+
exit ${1:-0}
|
183
|
+
}
|
184
|
+
|
185
|
+
#{bash_helpers}
|
186
|
+
|
187
|
+
#{before_script}
|
188
|
+
|
189
|
+
echo "Script starting..."
|
190
|
+
#{run_script} &
|
191
|
+
SCRIPT_PID=$!
|
192
|
+
|
193
|
+
#{after_script}
|
194
|
+
|
195
|
+
# Create the connection yaml file
|
196
|
+
create_yml
|
197
|
+
|
198
|
+
# Wait for script process to finish
|
199
|
+
wait ${SCRIPT_PID} || clean_up 1
|
200
|
+
|
201
|
+
# Exit cleanly
|
202
|
+
clean_up
|
203
|
+
EOT
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "ood_core/refinements/hash_extensions"
|
2
|
+
|
3
|
+
module OodCore
|
4
|
+
module BatchConnect
|
5
|
+
class Factory
|
6
|
+
using Refinements::HashExtensions
|
7
|
+
|
8
|
+
# Build the basic template from a configuration
|
9
|
+
# @param config [#to_h] the configuration for the batch connect template
|
10
|
+
def self.build_basic(config)
|
11
|
+
context = config.to_h.symbolize_keys.reject { |k, _| k == :template }
|
12
|
+
Templates::Basic.new(context)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Templates
|
17
|
+
# A batch connect template that expects to start up a basic web server
|
18
|
+
# within a batch job
|
19
|
+
class Basic < Template
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require "ood_core/refinements/hash_extensions"
|
2
|
+
|
3
|
+
module OodCore
|
4
|
+
module BatchConnect
|
5
|
+
class Factory
|
6
|
+
using Refinements::HashExtensions
|
7
|
+
|
8
|
+
# Build the VNC template from a configuration
|
9
|
+
# @param config [#to_h] the configuration for the batch connect template
|
10
|
+
def self.build_vnc(config)
|
11
|
+
context = config.to_h.symbolize_keys.reject { |k, _| k == :template }
|
12
|
+
Templates::VNC.new(context)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Templates
|
17
|
+
# A batch connect template that starts up a VNC server within a batch job
|
18
|
+
class VNC < Template
|
19
|
+
# @param context [#to_h] the context used to render the template
|
20
|
+
# @option context [#to_sym, Array<#to_sym>] :conn_params ([]) A list of
|
21
|
+
# connection parameters added to the connection file (`:host`,
|
22
|
+
# `:port`, `:password`, `:spassword`, `:display` and `:websocket`
|
23
|
+
# will always exist)
|
24
|
+
# @option context [#to_s] :websockify_cmd
|
25
|
+
# ("${WEBSOCKIFY_CMD:-/opt/websockify/run}") the path to the
|
26
|
+
# websockify script (assumes you don't modify `:after_script`)
|
27
|
+
# @option context [#to_s] :vnc_log ("vnc.log") path to vnc server log
|
28
|
+
# file (assumes you don't modify `:before_script` or `:after_script`)
|
29
|
+
# @option context [#to_s] :vnc_passwd ("vnc.passwd") path to the file
|
30
|
+
# generated that contains the encrypted vnc password (assumes you
|
31
|
+
# don't modify `:before_script`)
|
32
|
+
# @option context [#to_s] :vnc_args arguments used when starting up the
|
33
|
+
# vnc server (overrides any specific vnc argument) (assumes you don't
|
34
|
+
# modify `:before_script`)
|
35
|
+
# @option context [#to_s] :name ("") name of the vnc server session
|
36
|
+
# (not set if blank or `:vnc_args` is set) (assumes you don't modify
|
37
|
+
# `:before_script`)
|
38
|
+
# @option context [#to_s] :geometry ("") resolution of vnc display (not
|
39
|
+
# set if blank or `:vnc_args` is set) (assumes you don't modify
|
40
|
+
# `:before_script`)
|
41
|
+
# @option context [#to_s] :dpi ("") dpi of vnc display (not set if
|
42
|
+
# blank or `:vnc_args` is set) (assumes you don't modify
|
43
|
+
# `:before_script`)
|
44
|
+
# @option context [#to_s] :fonts ("") command delimited list of fonts
|
45
|
+
# available in vnc display (not set if blank or `:vnc_args` is set)
|
46
|
+
# (assumes you don't modify `:before_script`)
|
47
|
+
# @option context [#to_s] :idle ("") timeout vnc server if no
|
48
|
+
# connection in this amount of time in seconds (not set if blank or
|
49
|
+
# `:vnc_args` is set) (assumes you don't modify `:before_script`)
|
50
|
+
# @option context [#to_s] :extra_args ("") any extra arguments used
|
51
|
+
# when initializing the vnc server process (not set if blank or
|
52
|
+
# `:vnc_args` is set) (assumes you don't modify `:before_script`)
|
53
|
+
# @option context [#to_s] :vnc_clean ("...") script used to clean up
|
54
|
+
# any active vnc sessions (assumes you don't modify `:before_script`
|
55
|
+
# or `:clean_script`)
|
56
|
+
# @see Template
|
57
|
+
def initialize(context = {})
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
# We need to know the VNC and websockify connection information
|
63
|
+
def conn_params
|
64
|
+
(super + [:display, :websocket, :spassword]).uniq
|
65
|
+
end
|
66
|
+
|
67
|
+
# Before running the main script, start up a VNC server and record
|
68
|
+
# the connection information
|
69
|
+
def before_script
|
70
|
+
<<-EOT.gsub(/^ {14}/, "")
|
71
|
+
# Setup one-time use passwords and initialize the VNC password
|
72
|
+
function change_passwd () {
|
73
|
+
echo "Setting VNC password..."
|
74
|
+
password=$(create_passwd 8)
|
75
|
+
spassword=${spassword:-$(create_passwd 8)}
|
76
|
+
(
|
77
|
+
umask 077
|
78
|
+
echo -ne "${password}\\n${spassword}" | vncpasswd -f > "#{vnc_passwd}"
|
79
|
+
)
|
80
|
+
}
|
81
|
+
change_passwd
|
82
|
+
|
83
|
+
# Start up vnc server (if at first you don't succeed, try, try again)
|
84
|
+
echo "Starting VNC server..."
|
85
|
+
for i in $(seq 1 10); do
|
86
|
+
# Clean up any old VNC sessions that weren't cleaned before
|
87
|
+
#{vnc_clean}
|
88
|
+
|
89
|
+
# Attempt to start VNC server
|
90
|
+
VNC_OUT=$(vncserver -log "#{vnc_log}" -rfbauth "#{vnc_passwd}" -nohttpd -noxstartup #{vnc_args} 2>&1)
|
91
|
+
VNC_PID=$(pgrep -s 0 Xvnc) # the script above will daemonize the Xvnc process
|
92
|
+
echo "${VNC_OUT}"
|
93
|
+
|
94
|
+
# Sometimes Xvnc hangs if it fails to find working disaply, we
|
95
|
+
# should kill it and try again
|
96
|
+
kill -0 ${VNC_PID} 2>/dev/null && [[ "${VNC_OUT}" =~ "Fatal server error" ]] && kill -TERM ${VNC_PID}
|
97
|
+
|
98
|
+
# Check that Xvnc process is running, if not assume it died and
|
99
|
+
# wait some random period of time before restarting
|
100
|
+
kill -0 ${VNC_PID} 2>/dev/null || sleep 0.$(random 1 9)s
|
101
|
+
|
102
|
+
# If running, then all is well and break out of loop
|
103
|
+
kill -0 ${VNC_PID} 2>/dev/null && break
|
104
|
+
done
|
105
|
+
|
106
|
+
# If we fail to start it after so many tries, then just give up
|
107
|
+
kill -0 ${VNC_PID} 2>/dev/null || clean_up 1
|
108
|
+
|
109
|
+
# Parse output for ports used
|
110
|
+
display=$(echo "${VNC_OUT}" | awk -F':' '/^Desktop/{print $NF}')
|
111
|
+
port=$((5900+display))
|
112
|
+
|
113
|
+
echo "Successfully started VNC server on ${host}:${port}..."
|
114
|
+
|
115
|
+
#{super}
|
116
|
+
EOT
|
117
|
+
end
|
118
|
+
|
119
|
+
# Run the script under the VNC server's display
|
120
|
+
def run_script
|
121
|
+
%(DISPLAY=:${display} #{super})
|
122
|
+
end
|
123
|
+
|
124
|
+
# After startup the main script, scan the VNC server log file for
|
125
|
+
# successful connections so that the password can be reset
|
126
|
+
def after_script
|
127
|
+
websockify_cmd = context.fetch(:websockify_cmd, "${WEBSOCKIFY_CMD:-/opt/websockify/run}").to_s
|
128
|
+
|
129
|
+
<<-EOT.gsub(/^ {14}/, "")
|
130
|
+
#{super}
|
131
|
+
|
132
|
+
# Launch websockify websocket server
|
133
|
+
echo "Starting websocket server..."
|
134
|
+
websocket=$(find_port)
|
135
|
+
#{websockify_cmd} -D ${websocket} localhost:${port}
|
136
|
+
|
137
|
+
# Set up background process that scans the log file for successful
|
138
|
+
# connections by users, and change the password after every
|
139
|
+
# connection
|
140
|
+
echo "Scanning VNC log file for user authentications..."
|
141
|
+
while read -r line; do
|
142
|
+
if [[ ${line} =~ "Full-control authentication enabled for" ]]; then
|
143
|
+
change_passwd
|
144
|
+
create_yml
|
145
|
+
fi
|
146
|
+
done < <(tail -f --pid=${SCRIPT_PID} "#{vnc_log}") &
|
147
|
+
EOT
|
148
|
+
end
|
149
|
+
|
150
|
+
# Clean up the running VNC server and any other stale VNC servers
|
151
|
+
def clean_script
|
152
|
+
<<-EOT.gsub(/^ {14}/, "")
|
153
|
+
#{super}
|
154
|
+
|
155
|
+
#{vnc_clean}
|
156
|
+
[[ -n ${display} ]] && vncserver -kill :${display}
|
157
|
+
EOT
|
158
|
+
end
|
159
|
+
|
160
|
+
# Log file for VNC server
|
161
|
+
def vnc_log
|
162
|
+
context.fetch(:vnc_log, "vnc.log").to_s
|
163
|
+
end
|
164
|
+
|
165
|
+
# Password file for VNC server
|
166
|
+
def vnc_passwd
|
167
|
+
context.fetch(:vnc_passwd, "vnc.passwd").to_s
|
168
|
+
end
|
169
|
+
|
170
|
+
# Arguments sent to `vncserver` command
|
171
|
+
def vnc_args
|
172
|
+
context.fetch(:vnc_args) do
|
173
|
+
name = context.fetch(:name, "").to_s
|
174
|
+
geometry = context.fetch(:geometry, "").to_s
|
175
|
+
dpi = context.fetch(:dpi, "").to_s
|
176
|
+
fonts = context.fetch(:fonts, "").to_s
|
177
|
+
idle = context.fetch(:idle, "").to_s
|
178
|
+
extra_args = context.fetch(:extra_args, "").to_s
|
179
|
+
|
180
|
+
args = []
|
181
|
+
args << "-name #{name}" unless name.empty?
|
182
|
+
args << "-geometry #{geometry}" unless geometry.empty?
|
183
|
+
args << "-dpi #{dpi}" unless dpi.empty?
|
184
|
+
args << "-fp #{fonts}" unless fonts.empty?
|
185
|
+
args << "-idletimeout #{idle}" unless idle.empty?
|
186
|
+
args << extra_args
|
187
|
+
|
188
|
+
args.join(" ")
|
189
|
+
end.to_s
|
190
|
+
end
|
191
|
+
|
192
|
+
# Clean up any stale VNC sessions
|
193
|
+
def vnc_clean
|
194
|
+
context.fetch(:vnc_clean) do
|
195
|
+
%(vncserver -list | awk '/^:/{system("kill -0 "$2" 2>/dev/null || vncserver -kill "$1)}')
|
196
|
+
end.to_s
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|