ood_core 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|