right_agent 0.17.2 → 1.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.
- data/lib/right_agent.rb +0 -1
- data/lib/right_agent/agent_config.rb +1 -1
- data/lib/right_agent/minimal.rb +8 -7
- data/lib/right_agent/monkey_patches.rb +4 -2
- data/lib/right_agent/monkey_patches/ruby_patch.rb +9 -9
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +2 -2
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +21 -51
- data/lib/right_agent/packets.rb +5 -1
- data/lib/right_agent/platform.rb +727 -299
- data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
- data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
- data/lib/right_agent/platform/unix/platform.rb +226 -0
- data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
- data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
- data/lib/right_agent/platform/windows/platform.rb +1808 -0
- data/right_agent.gemspec +13 -8
- data/spec/platform/spec_helper.rb +216 -0
- data/spec/platform/unix/darwin/platform_spec.rb +181 -0
- data/spec/platform/unix/linux/platform_spec.rb +540 -0
- data/spec/platform/unix/spec_helper.rb +149 -0
- data/spec/platform/windows/mingw/platform_spec.rb +222 -0
- data/spec/platform/windows/mswin/platform_spec.rb +259 -0
- data/spec/platform/windows/spec_helper.rb +720 -0
- metadata +45 -30
- data/lib/right_agent/platform/darwin.rb +0 -285
- data/lib/right_agent/platform/linux.rb +0 -537
- data/lib/right_agent/platform/windows.rb +0 -1384
- data/spec/platform/darwin_spec.rb +0 -13
- data/spec/platform/linux_spec.rb +0 -38
- data/spec/platform/linux_volume_manager_spec.rb +0 -201
- data/spec/platform/platform_spec.rb +0 -80
- data/spec/platform/windows_spec.rb +0 -13
- data/spec/platform/windows_volume_manager_spec.rb +0 -318
metadata
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Lee Kirchhoff
|
9
9
|
- Raphael Simon
|
10
10
|
- Tony Spataro
|
11
|
+
- Scott Messier
|
11
12
|
autorequire:
|
12
13
|
bindir: bin
|
13
14
|
cert_chain: []
|
14
|
-
date: 2013-10-
|
15
|
+
date: 2013-10-29 00:00:00.000000000 Z
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
17
18
|
name: right_support
|
@@ -52,16 +53,16 @@ dependencies:
|
|
52
53
|
- !ruby/object:Gem::Version
|
53
54
|
version: '0.7'
|
54
55
|
- !ruby/object:Gem::Dependency
|
55
|
-
name:
|
56
|
+
name: eventmachine
|
56
57
|
requirement: !ruby/object:Gem::Requirement
|
57
58
|
none: false
|
58
59
|
requirements:
|
59
60
|
- - ! '>='
|
60
61
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
62
|
-
- -
|
62
|
+
version: 0.12.10
|
63
|
+
- - <
|
63
64
|
- !ruby/object:Gem::Version
|
64
|
-
version:
|
65
|
+
version: '2.0'
|
65
66
|
type: :runtime
|
66
67
|
prerelease: false
|
67
68
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -69,32 +70,42 @@ dependencies:
|
|
69
70
|
requirements:
|
70
71
|
- - ! '>='
|
71
72
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
73
|
-
- -
|
73
|
+
version: 0.12.10
|
74
|
+
- - <
|
74
75
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
76
|
+
version: '2.0'
|
76
77
|
- !ruby/object:Gem::Dependency
|
77
|
-
name:
|
78
|
+
name: net-ssh
|
78
79
|
requirement: !ruby/object:Gem::Requirement
|
79
80
|
none: false
|
80
81
|
requirements:
|
81
|
-
- -
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: 0.12.10
|
84
|
-
- - <
|
82
|
+
- - ~>
|
85
83
|
- !ruby/object:Gem::Version
|
86
84
|
version: '2.0'
|
87
85
|
type: :runtime
|
88
86
|
prerelease: false
|
89
87
|
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '2.0'
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: ffi
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
90
96
|
none: false
|
91
97
|
requirements:
|
92
98
|
- - ! '>='
|
93
99
|
- !ruby/object:Gem::Version
|
94
|
-
version: 0
|
95
|
-
|
100
|
+
version: '0'
|
101
|
+
type: :runtime
|
102
|
+
prerelease: false
|
103
|
+
version_requirements: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
96
107
|
- !ruby/object:Gem::Version
|
97
|
-
version: '
|
108
|
+
version: '0'
|
98
109
|
- !ruby/object:Gem::Dependency
|
99
110
|
name: msgpack
|
100
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,13 +129,13 @@ dependencies:
|
|
118
129
|
- !ruby/object:Gem::Version
|
119
130
|
version: '0.6'
|
120
131
|
- !ruby/object:Gem::Dependency
|
121
|
-
name:
|
132
|
+
name: json
|
122
133
|
requirement: !ruby/object:Gem::Requirement
|
123
134
|
none: false
|
124
135
|
requirements:
|
125
136
|
- - ~>
|
126
137
|
- !ruby/object:Gem::Version
|
127
|
-
version: '
|
138
|
+
version: '1.4'
|
128
139
|
type: :runtime
|
129
140
|
prerelease: false
|
130
141
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -132,7 +143,7 @@ dependencies:
|
|
132
143
|
requirements:
|
133
144
|
- - ~>
|
134
145
|
- !ruby/object:Gem::Version
|
135
|
-
version: '
|
146
|
+
version: '1.4'
|
136
147
|
description: ! 'RightAgent provides a foundation for running an agent on a server
|
137
148
|
to interface
|
138
149
|
|
@@ -224,9 +235,12 @@ files:
|
|
224
235
|
- lib/right_agent/payload_formatter.rb
|
225
236
|
- lib/right_agent/pid_file.rb
|
226
237
|
- lib/right_agent/platform.rb
|
227
|
-
- lib/right_agent/platform/darwin.rb
|
228
|
-
- lib/right_agent/platform/linux.rb
|
229
|
-
- lib/right_agent/platform/
|
238
|
+
- lib/right_agent/platform/unix/darwin/platform.rb
|
239
|
+
- lib/right_agent/platform/unix/linux/platform.rb
|
240
|
+
- lib/right_agent/platform/unix/platform.rb
|
241
|
+
- lib/right_agent/platform/windows/mingw/platform.rb
|
242
|
+
- lib/right_agent/platform/windows/mswin/platform.rb
|
243
|
+
- lib/right_agent/platform/windows/platform.rb
|
230
244
|
- lib/right_agent/scripts/agent_controller.rb
|
231
245
|
- lib/right_agent/scripts/agent_deployer.rb
|
232
246
|
- lib/right_agent/scripts/common_parser.rb
|
@@ -282,12 +296,13 @@ files:
|
|
282
296
|
- spec/multiplexer_spec.rb
|
283
297
|
- spec/operation_result_spec.rb
|
284
298
|
- spec/packets_spec.rb
|
285
|
-
- spec/platform/
|
286
|
-
- spec/platform/
|
287
|
-
- spec/platform/
|
288
|
-
- spec/platform/
|
289
|
-
- spec/platform/
|
290
|
-
- spec/platform/
|
299
|
+
- spec/platform/spec_helper.rb
|
300
|
+
- spec/platform/unix/darwin/platform_spec.rb
|
301
|
+
- spec/platform/unix/linux/platform_spec.rb
|
302
|
+
- spec/platform/unix/spec_helper.rb
|
303
|
+
- spec/platform/windows/mingw/platform_spec.rb
|
304
|
+
- spec/platform/windows/mswin/platform_spec.rb
|
305
|
+
- spec/platform/windows/spec_helper.rb
|
291
306
|
- spec/results_mock.rb
|
292
307
|
- spec/secure_identity_spec.rb
|
293
308
|
- spec/security/cached_certificate_store_proxy_spec.rb
|
@@ -331,7 +346,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
331
346
|
version: '0'
|
332
347
|
segments:
|
333
348
|
- 0
|
334
|
-
hash:
|
349
|
+
hash: -614048147788141961
|
335
350
|
requirements: []
|
336
351
|
rubyforge_project:
|
337
352
|
rubygems_version: 1.8.26
|
@@ -1,285 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright (c) 2009-2011 RightScale Inc
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
-
# a copy of this software and associated documentation files (the
|
6
|
-
# "Software"), to deal in the Software without restriction, including
|
7
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
-
# the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be
|
13
|
-
# included in all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
-
|
23
|
-
module RightScale
|
24
|
-
|
25
|
-
# Mac OS specific implementation
|
26
|
-
class Platform
|
27
|
-
|
28
|
-
attr_reader :flavor, :release
|
29
|
-
|
30
|
-
# Initialize flavor and release
|
31
|
-
def init
|
32
|
-
@flavor = 'mac_os_x'
|
33
|
-
@release = `sw_vers -productVersion`
|
34
|
-
end
|
35
|
-
|
36
|
-
class Filesystem
|
37
|
-
|
38
|
-
# Is given command available in the PATH?
|
39
|
-
#
|
40
|
-
# === Parameters
|
41
|
-
# command_name(String):: Name of command to be tested
|
42
|
-
#
|
43
|
-
# === Return
|
44
|
-
# true:: If command is in path
|
45
|
-
# false:: Otherwise
|
46
|
-
def has_executable_in_path(command_name)
|
47
|
-
return nil != find_executable_in_path(command_name)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Finds the given command name in the PATH. this emulates the 'which'
|
51
|
-
# command from linux (without the terminating newline).
|
52
|
-
#
|
53
|
-
# === Parameters
|
54
|
-
# command_name(String):: Name of command to be tested
|
55
|
-
#
|
56
|
-
# === Return
|
57
|
-
# path to first matching executable file in PATH or nil
|
58
|
-
def find_executable_in_path(command_name)
|
59
|
-
ENV['PATH'].split(/;|:/).each do |dir|
|
60
|
-
path = File.join(dir, command_name)
|
61
|
-
return path if File.executable?(path)
|
62
|
-
end
|
63
|
-
return nil
|
64
|
-
end
|
65
|
-
|
66
|
-
# Directory containing generated agent configuration files
|
67
|
-
# @deprecated
|
68
|
-
def cfg_dir
|
69
|
-
warn "cfg_dir is deprecated; please use right_agent_cfg_dir"
|
70
|
-
right_agent_cfg_dir
|
71
|
-
end
|
72
|
-
|
73
|
-
# RightScale state directory for the current platform
|
74
|
-
# @deprecated
|
75
|
-
def right_scale_state_dir
|
76
|
-
warn "right_scale_state_dir is deprecated; please use either right_scale_static_state_dir or right_agent_dynamic_state_dir"
|
77
|
-
right_scale_static_state_dir
|
78
|
-
end
|
79
|
-
|
80
|
-
# Directory containing generated agent configuration files
|
81
|
-
def right_agent_cfg_dir
|
82
|
-
'/var/lib/rightscale/right_agent'
|
83
|
-
end
|
84
|
-
|
85
|
-
# Static (time-invariant) state that is common to all RightScale apps/agents
|
86
|
-
def right_scale_static_state_dir
|
87
|
-
'/etc/rightscale.d'
|
88
|
-
end
|
89
|
-
|
90
|
-
# Static (time-invariant) state that is specific to RightLink
|
91
|
-
def right_link_static_state_dir
|
92
|
-
'/etc/rightscale.d/right_link'
|
93
|
-
end
|
94
|
-
|
95
|
-
# Dynamic, persistent runtime state that is specific to RightLink
|
96
|
-
def right_link_dynamic_state_dir
|
97
|
-
'/var/lib/rightscale/right_link'
|
98
|
-
end
|
99
|
-
|
100
|
-
# Data which is awaiting some kind of later processing
|
101
|
-
def spool_dir
|
102
|
-
'/var/spool'
|
103
|
-
end
|
104
|
-
|
105
|
-
def ssh_cfg_dir
|
106
|
-
# TODO This is a guess, but since we don't have Darwin instances
|
107
|
-
# it may need to be corrected later.
|
108
|
-
'/etc/ssh'
|
109
|
-
end
|
110
|
-
|
111
|
-
# Cached data from applications. Such data is locally generated as a
|
112
|
-
# result of time-consuming I/O or calculation. The application must
|
113
|
-
# be able to regenerate or restore the data.
|
114
|
-
def cache_dir
|
115
|
-
'/var/cache'
|
116
|
-
end
|
117
|
-
|
118
|
-
# System logs
|
119
|
-
def log_dir
|
120
|
-
'/var/log'
|
121
|
-
end
|
122
|
-
|
123
|
-
# Source code, for reference purposes and for development.
|
124
|
-
def source_code_dir
|
125
|
-
'/usr/src'
|
126
|
-
end
|
127
|
-
|
128
|
-
# Temporary files.
|
129
|
-
def temp_dir
|
130
|
-
'/tmp'
|
131
|
-
end
|
132
|
-
|
133
|
-
# Path to place pid files
|
134
|
-
def pid_dir
|
135
|
-
'/var/run'
|
136
|
-
end
|
137
|
-
|
138
|
-
# Path to right link configuration and internal usage scripts
|
139
|
-
def private_bin_dir
|
140
|
-
'/opt/rightscale/bin'
|
141
|
-
end
|
142
|
-
|
143
|
-
def sandbox_dir
|
144
|
-
'/opt/rightscale/sandbox'
|
145
|
-
end
|
146
|
-
|
147
|
-
# for windows compatibility; has no significance in darwin
|
148
|
-
def long_path_to_short_path(long_path)
|
149
|
-
return long_path
|
150
|
-
end
|
151
|
-
|
152
|
-
# for windows compatibility; has no significance in darwin
|
153
|
-
def pretty_path(path, native_fs_flag = false)
|
154
|
-
return path
|
155
|
-
end
|
156
|
-
|
157
|
-
# for windows compatibility; has no significance in linux
|
158
|
-
def ensure_local_drive_path(path, temp_dir_name)
|
159
|
-
return path
|
160
|
-
end
|
161
|
-
|
162
|
-
# for windows compatibility; just use File.symlink on Mac
|
163
|
-
def create_symlink(old_name, new_name)
|
164
|
-
File.symlink(old_name, new_name)
|
165
|
-
end
|
166
|
-
end # Filesystem
|
167
|
-
|
168
|
-
# Provides utilities for managing volumes (disks).
|
169
|
-
class VolumeManager
|
170
|
-
def initialize
|
171
|
-
raise "not yet implemented"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
class Shell
|
176
|
-
|
177
|
-
NULL_OUTPUT_NAME = "/dev/null"
|
178
|
-
|
179
|
-
def format_script_file_name(partial_script_file_path, default_extension = nil)
|
180
|
-
# shell file extensions are not required in darwin assuming the script
|
181
|
-
# contains a shebang. if not, the error should be obvious.
|
182
|
-
return partial_script_file_path
|
183
|
-
end
|
184
|
-
|
185
|
-
def format_executable_command(executable_file_path, *arguments)
|
186
|
-
escaped = []
|
187
|
-
[executable_file_path, arguments].flatten.each do |arg|
|
188
|
-
value = arg.to_s
|
189
|
-
needs_escape = value.index(" ") || value.index("\"") || value.index("'")
|
190
|
-
escaped << (needs_escape ? "\"#{value.gsub("\"", "\\\"")}\"" : value)
|
191
|
-
end
|
192
|
-
return escaped.join(" ")
|
193
|
-
end
|
194
|
-
|
195
|
-
def format_shell_command(shell_script_file_path, *arguments)
|
196
|
-
# shell files containing shebang are directly executable in darwin, so
|
197
|
-
# assume our scripts have shebang. if not, the error should be obvious.
|
198
|
-
return format_executable_command(shell_script_file_path, arguments)
|
199
|
-
end
|
200
|
-
|
201
|
-
def format_redirect_stdout(cmd, target = NULL_OUTPUT_NAME)
|
202
|
-
return cmd + " 1>#{target}"
|
203
|
-
end
|
204
|
-
|
205
|
-
def format_redirect_stderr(cmd, target = NULL_OUTPUT_NAME)
|
206
|
-
return cmd + " 2>#{target}"
|
207
|
-
end
|
208
|
-
|
209
|
-
def format_redirect_both(cmd, target = NULL_OUTPUT_NAME)
|
210
|
-
return cmd + " 1>#{target} 2>&1"
|
211
|
-
end
|
212
|
-
|
213
|
-
def sandbox_ruby
|
214
|
-
"#{RightScale::Platform.filesystem.sandbox_dir}/bin/ruby"
|
215
|
-
end
|
216
|
-
|
217
|
-
# Gets the current system uptime.
|
218
|
-
#
|
219
|
-
# === Return
|
220
|
-
# the time the machine has been up in seconds, 0 if there was an error.
|
221
|
-
def uptime
|
222
|
-
return (Time.now.to_i.to_f - booted_at.to_f) rescue 0.0
|
223
|
-
end
|
224
|
-
|
225
|
-
# Gets the time at which the system was booted
|
226
|
-
#
|
227
|
-
# === Return
|
228
|
-
# the UTC timestamp at which the system was booted
|
229
|
-
def booted_at
|
230
|
-
match = /sec = ([0-9]+)/.match(`sysctl kern.boottime`)
|
231
|
-
|
232
|
-
if match && (match[1].to_i > 0)
|
233
|
-
return match[1].to_i
|
234
|
-
else
|
235
|
-
return nil
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
end # Shell
|
240
|
-
|
241
|
-
class Controller
|
242
|
-
# Shutdown machine now
|
243
|
-
def shutdown
|
244
|
-
`shutdown -h now`
|
245
|
-
end
|
246
|
-
|
247
|
-
# Reboot machine now
|
248
|
-
def reboot
|
249
|
-
`shutdown -r now`
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
class Rng
|
254
|
-
def pseudorandom_bytes(count)
|
255
|
-
f = File.open('/dev/urandom', 'r')
|
256
|
-
bytes = f.read(count)
|
257
|
-
f.close
|
258
|
-
|
259
|
-
bytes
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
class Process
|
264
|
-
# queries resident set size (current working set size in Windows).
|
265
|
-
#
|
266
|
-
# === Parameters
|
267
|
-
# pid(Fixnum):: process ID or nil for current process
|
268
|
-
#
|
269
|
-
# === Return
|
270
|
-
# result(Fixnum):: current set size in KB
|
271
|
-
def resident_set_size(pid=nil)
|
272
|
-
pid = $$ unless pid
|
273
|
-
return `ps -o rss= -p #{pid}`.to_i
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
class Installer
|
278
|
-
def install(packages)
|
279
|
-
raise "not yet implemented"
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
end # Platform
|
284
|
-
|
285
|
-
end # RightScale
|
@@ -1,537 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright (c) 2009-2011 RightScale Inc
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
-
# a copy of this software and associated documentation files (the
|
6
|
-
# "Software"), to deal in the Software without restriction, including
|
7
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
-
# the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be
|
13
|
-
# included in all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
-
|
23
|
-
module RightScale
|
24
|
-
|
25
|
-
# Linux specific implementation
|
26
|
-
class Platform
|
27
|
-
|
28
|
-
FEDORA_REL = '/etc/fedora-release'
|
29
|
-
FEDORA_SIG = /Fedora release ([0-9]+) \(.*\)/
|
30
|
-
|
31
|
-
attr_reader :flavor, :release, :codename
|
32
|
-
|
33
|
-
# Initialize flavor, release and codename
|
34
|
-
def init
|
35
|
-
system('lsb_release --help > /dev/null 2>&1')
|
36
|
-
if $?.success?
|
37
|
-
# Use the lsb_release utility if it's available
|
38
|
-
@flavor = `lsb_release -is`.strip.downcase
|
39
|
-
@release = `lsb_release -rs`.strip
|
40
|
-
@codename = `lsb_release -cs`.strip
|
41
|
-
elsif File.exist?(FEDORA_REL) && (match = FEDORA_SIG.match(File.read(FEDORA_REL)))
|
42
|
-
# Parse the fedora-release file if it exists
|
43
|
-
@flavor = 'fedora'
|
44
|
-
@release = match[1]
|
45
|
-
@codename = match[2]
|
46
|
-
else
|
47
|
-
@distro = @release = @codename = 'unknown'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Is this machine running Ubuntu?
|
52
|
-
#
|
53
|
-
# === Return
|
54
|
-
# true:: If Linux flavor is Ubuntu
|
55
|
-
# false:: Otherwise
|
56
|
-
def ubuntu?
|
57
|
-
@flavor =~ /ubuntu/
|
58
|
-
end
|
59
|
-
|
60
|
-
# Is this machine running CentOS?
|
61
|
-
#
|
62
|
-
# === Return
|
63
|
-
# true:: If Linux flavor is CentOS
|
64
|
-
# false:: Otherwise
|
65
|
-
def centos?
|
66
|
-
@flavor =~ /centos/
|
67
|
-
end
|
68
|
-
|
69
|
-
# Is this machine running Suse
|
70
|
-
#
|
71
|
-
# === Return
|
72
|
-
# true:: If Linux flavor is Suse
|
73
|
-
# false:: Otherwise
|
74
|
-
def suse?
|
75
|
-
@flavor =~ /suse/
|
76
|
-
end
|
77
|
-
|
78
|
-
# Is this machine running rhel?
|
79
|
-
#
|
80
|
-
# === Return
|
81
|
-
# true:: If Linux flavor is rhel
|
82
|
-
# false:: Otherwise
|
83
|
-
def rhel?
|
84
|
-
@flavor =~ /redhatenterpriseserver/
|
85
|
-
end
|
86
|
-
|
87
|
-
class Filesystem
|
88
|
-
|
89
|
-
# Is given command available in the PATH?
|
90
|
-
#
|
91
|
-
# === Parameters
|
92
|
-
# command_name(String):: Name of command to be tested
|
93
|
-
#
|
94
|
-
# === Return
|
95
|
-
# true:: If command is in path
|
96
|
-
# false:: Otherwise
|
97
|
-
def has_executable_in_path(command_name)
|
98
|
-
return nil != find_executable_in_path(command_name)
|
99
|
-
end
|
100
|
-
|
101
|
-
# Finds the given command name in the PATH. this emulates the 'which'
|
102
|
-
# command from linux (without the terminating newline).
|
103
|
-
#
|
104
|
-
# === Parameters
|
105
|
-
# command_name(String):: Name of command to be tested
|
106
|
-
#
|
107
|
-
# === Return
|
108
|
-
# path to first matching executable file in PATH or nil
|
109
|
-
def find_executable_in_path(command_name)
|
110
|
-
ENV['PATH'].split(/;|:/).each do |dir|
|
111
|
-
path = File.join(dir, command_name)
|
112
|
-
return path if File.executable?(path)
|
113
|
-
end
|
114
|
-
return nil
|
115
|
-
end
|
116
|
-
|
117
|
-
# Directory containing generated agent configuration files
|
118
|
-
# @deprecated
|
119
|
-
def cfg_dir
|
120
|
-
warn "cfg_dir is deprecated; please use right_agent_cfg_dir"
|
121
|
-
right_agent_cfg_dir
|
122
|
-
end
|
123
|
-
|
124
|
-
# RightScale state directory for the current platform
|
125
|
-
# @deprecated
|
126
|
-
def right_scale_state_dir
|
127
|
-
warn "right_scale_state_dir is deprecated; please use either right_scale_static_state_dir or right_agent_dynamic_state_dir"
|
128
|
-
right_scale_static_state_dir
|
129
|
-
end
|
130
|
-
|
131
|
-
# Directory containing generated agent configuration files
|
132
|
-
def right_agent_cfg_dir
|
133
|
-
'/var/lib/rightscale/right_agent'
|
134
|
-
end
|
135
|
-
|
136
|
-
# Static (time-invariant) state that is common to all RightScale apps/agents
|
137
|
-
def right_scale_static_state_dir
|
138
|
-
'/etc/rightscale.d'
|
139
|
-
end
|
140
|
-
|
141
|
-
# Static (time-invariant) state that is specific to RightLink
|
142
|
-
def right_link_static_state_dir
|
143
|
-
'/etc/rightscale.d/right_link'
|
144
|
-
end
|
145
|
-
|
146
|
-
# Dynamic, persistent runtime state that is specific to RightLink
|
147
|
-
def right_link_dynamic_state_dir
|
148
|
-
'/var/lib/rightscale/right_link'
|
149
|
-
end
|
150
|
-
|
151
|
-
# Data which is awaiting some kind of later processing
|
152
|
-
def spool_dir
|
153
|
-
'/var/spool'
|
154
|
-
end
|
155
|
-
|
156
|
-
def ssh_cfg_dir
|
157
|
-
'/etc/ssh'
|
158
|
-
end
|
159
|
-
|
160
|
-
# Cached data from applications. Such data is locally generated as a
|
161
|
-
# result of time-consuming I/O or calculation. The application must
|
162
|
-
# be able to regenerate or restore the data.
|
163
|
-
def cache_dir
|
164
|
-
'/var/cache'
|
165
|
-
end
|
166
|
-
|
167
|
-
# System logs
|
168
|
-
def log_dir
|
169
|
-
'/var/log'
|
170
|
-
end
|
171
|
-
|
172
|
-
# Source code, for reference purposes and for development.
|
173
|
-
def source_code_dir
|
174
|
-
'/usr/src'
|
175
|
-
end
|
176
|
-
|
177
|
-
# Temporary files.
|
178
|
-
def temp_dir
|
179
|
-
'/tmp'
|
180
|
-
end
|
181
|
-
|
182
|
-
# Path to place pid files
|
183
|
-
def pid_dir
|
184
|
-
'/var/run'
|
185
|
-
end
|
186
|
-
|
187
|
-
# Path to right link configuration and internal usage scripts
|
188
|
-
def private_bin_dir
|
189
|
-
'/opt/rightscale/bin'
|
190
|
-
end
|
191
|
-
|
192
|
-
def sandbox_dir
|
193
|
-
'/opt/rightscale/sandbox'
|
194
|
-
end
|
195
|
-
|
196
|
-
# for windows compatibility; has no significance in linux
|
197
|
-
def long_path_to_short_path(long_path)
|
198
|
-
return long_path
|
199
|
-
end
|
200
|
-
|
201
|
-
# for windows compatibility; has no significance in linux
|
202
|
-
def pretty_path(path, native_fs_flag = false)
|
203
|
-
return path
|
204
|
-
end
|
205
|
-
|
206
|
-
# for windows compatibility; has no significance in linux
|
207
|
-
def ensure_local_drive_path(path, temp_dir_name)
|
208
|
-
return path
|
209
|
-
end
|
210
|
-
|
211
|
-
# for windows compatibility; just use File.symlink on Linux
|
212
|
-
def create_symlink(old_name, new_name)
|
213
|
-
File.symlink(old_name, new_name)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
# Provides utilities for managing volumes (disks).
|
218
|
-
class VolumeManager
|
219
|
-
|
220
|
-
class ParserError < Exception; end
|
221
|
-
class VolumeError < Exception; end
|
222
|
-
|
223
|
-
def initialize
|
224
|
-
|
225
|
-
end
|
226
|
-
|
227
|
-
# Gets a list of currently visible volumes in the form:
|
228
|
-
# [{:device, :label, :uuid, :type, :filesystem}]
|
229
|
-
#
|
230
|
-
# === Parameters
|
231
|
-
# conditions(Hash):: hash of conditions to match, or nil (default)
|
232
|
-
#
|
233
|
-
# === Return
|
234
|
-
# result(Array):: array of volume hashes, or an empty array
|
235
|
-
#
|
236
|
-
# === Raise
|
237
|
-
# VolumeError:: on failure to execute `blkid` to obtain raw output
|
238
|
-
# ParserError:: on failure to parse volume list
|
239
|
-
def volumes(conditions = nil)
|
240
|
-
exit_code, blkid_resp = blocking_popen('blkid')
|
241
|
-
raise VolumeError.new("Failed to list volumes exit code = #{exit_code}\nblkid\n#{blkid_resp}") unless exit_code == 0
|
242
|
-
return parse_volumes(blkid_resp, conditions)
|
243
|
-
end
|
244
|
-
|
245
|
-
# Parses raw output from `blkid` into a hash of volumes
|
246
|
-
#
|
247
|
-
# The hash will contain the device name with a key of :device, and each key value pair
|
248
|
-
# for the device. In order to keep parity with the Windows VolumeManager.parse_volumes
|
249
|
-
# method, the :type key will be duplicated as :filesystem
|
250
|
-
#
|
251
|
-
# Example of raw output from `blkid`
|
252
|
-
#
|
253
|
-
# /dev/xvdh1: SEC_TYPE="msdos" LABEL="METADATA" UUID="681B-8C5D" TYPE="vfat"
|
254
|
-
# /dev/xvdb1: LABEL="SWAP-xvdb1" UUID="d51fcca0-6b10-4934-a572-f3898dfd8840" TYPE="swap"
|
255
|
-
# /dev/xvda1: UUID="f4746f9c-0557-4406-9267-5e918e87ca2e" TYPE="ext3"
|
256
|
-
# /dev/xvda2: UUID="14d88b9e-9fe6-4974-a8d6-180acdae4016" TYPE="ext3"
|
257
|
-
#
|
258
|
-
# === Parameters
|
259
|
-
# output_text(String):: raw output from `blkid`
|
260
|
-
# conditions(Hash):: hash of conditions to match, or nil (default)
|
261
|
-
#
|
262
|
-
# === Return
|
263
|
-
# result(Array):: array of volume hashes, or an empty array
|
264
|
-
#
|
265
|
-
# === Raise
|
266
|
-
# ParserError:: on failure to parse volume list
|
267
|
-
def parse_volumes(output_text, conditions = nil)
|
268
|
-
results = []
|
269
|
-
output_text.lines.each do |line|
|
270
|
-
volume = {}
|
271
|
-
line_regex = /^([\/a-z0-9_\-\.]+):(.*)/
|
272
|
-
volmatch = line_regex.match(line)
|
273
|
-
raise ParserError.new("Failed to parse volume info from #{line.inspect} using #{line_regex.inspect}") unless volmatch
|
274
|
-
volume[:device] = volmatch[1]
|
275
|
-
volmatch[2].split(' ').each do |pair|
|
276
|
-
pair_regex = /([a-zA-Z_\-]+)=(.*)/
|
277
|
-
match = pair_regex.match(pair)
|
278
|
-
raise ParserError.new("Failed to parse volume info from #{pair} using #{pair_regex.inspect}") unless match
|
279
|
-
volume[:"#{match[1].downcase}"] = match[2].gsub('"', '')
|
280
|
-
# Make this as much like the windows output as possible
|
281
|
-
if match[1] == "TYPE"
|
282
|
-
volume[:filesystem] = match[2].gsub('"', '')
|
283
|
-
end
|
284
|
-
end
|
285
|
-
if conditions
|
286
|
-
matched = true
|
287
|
-
conditions.each do |key,value|
|
288
|
-
|
289
|
-
unless volume[key] == value
|
290
|
-
matched = false
|
291
|
-
break
|
292
|
-
end
|
293
|
-
end
|
294
|
-
results << volume if matched
|
295
|
-
else
|
296
|
-
results << volume
|
297
|
-
end
|
298
|
-
end
|
299
|
-
results
|
300
|
-
end
|
301
|
-
|
302
|
-
# Mounts a volume (returned by VolumeManager.volumes) to the mountpoint specified.
|
303
|
-
#
|
304
|
-
# === Parameters
|
305
|
-
# volume(Hash):: the volume hash returned by VolumeManager.volumes
|
306
|
-
# mountpoint(String):: the exact path where the device will be mounted ex: /mnt
|
307
|
-
#
|
308
|
-
# === Return
|
309
|
-
# always true
|
310
|
-
#
|
311
|
-
# === Raise
|
312
|
-
# ArgumentError:: on invalid parameters
|
313
|
-
# VolumeError:: on a failure to mount the device
|
314
|
-
def mount_volume(volume, mountpoint)
|
315
|
-
raise ArgumentError.new("Invalid volume = #{volume.inspect}") unless volume.is_a?(Hash) && volume[:device]
|
316
|
-
exit_code, mount_list_output = blocking_popen('mount')
|
317
|
-
raise VolumeError.new("Failed interrogation of current mounts; Exit Status: #{exit_code}\nError: #{mount_list_output}") unless exit_code == 0
|
318
|
-
|
319
|
-
device_match = /^#{volume[:device]} on (.+?)\s/.match(mount_list_output)
|
320
|
-
mountpoint_from_device_match = device_match ? device_match[1] : mountpoint
|
321
|
-
unless (mountpoint_from_device_match && mountpoint_from_device_match == mountpoint)
|
322
|
-
raise VolumeError.new("Attempted to mount volume \"#{volume[:device]}\" at \"#{mountpoint}\" but it was already mounted at #{mountpoint_from_device_match}")
|
323
|
-
end
|
324
|
-
|
325
|
-
mountpoint_match = /^(.+?) on #{mountpoint}/.match(mount_list_output)
|
326
|
-
device_from_mountpoint_match = mountpoint_match ? mountpoint_match[1] : volume[:device]
|
327
|
-
unless (device_from_mountpoint_match && device_from_mountpoint_match == volume[:device])
|
328
|
-
raise VolumeError.new("Attempted to mount volume \"#{volume[:device]}\" at \"#{mountpoint}\" but \"#{device_from_mountpoint_match}\" was already mounted there.")
|
329
|
-
end
|
330
|
-
|
331
|
-
# The volume is already mounted at the correct mountpoint
|
332
|
-
return true if /^#{volume[:device]} on #{mountpoint}/.match(mount_list_output)
|
333
|
-
|
334
|
-
# TODO: Maybe validate that the mountpoint is valid *nix path?
|
335
|
-
exit_code, mount_output = blocking_popen("mount -t #{volume[:filesystem].strip} #{volume[:device]} #{mountpoint}")
|
336
|
-
raise VolumeError.new("Failed to mount volume to \"#{mountpoint}\" with device \"#{volume[:device]}\"; Exit Status: #{exit_code}\nError: #{mount_output}") unless exit_code == 0
|
337
|
-
return true
|
338
|
-
end
|
339
|
-
|
340
|
-
# Runs the specified command synchronously using IO.popen
|
341
|
-
#
|
342
|
-
# === Parameters
|
343
|
-
# command(String):: system command to be executed
|
344
|
-
#
|
345
|
-
# === Return
|
346
|
-
# result(Array):: tuple of [exit_code, output_text]
|
347
|
-
def blocking_popen(command)
|
348
|
-
output_text = ""
|
349
|
-
IO.popen(command) do |io|
|
350
|
-
output_text = io.read
|
351
|
-
end
|
352
|
-
return $?.exitstatus, output_text
|
353
|
-
end
|
354
|
-
end # VolumeManager
|
355
|
-
|
356
|
-
class Shell
|
357
|
-
|
358
|
-
NULL_OUTPUT_NAME = "/dev/null"
|
359
|
-
|
360
|
-
def format_script_file_name(partial_script_file_path, default_extension = nil)
|
361
|
-
# shell file extensions are not required in linux assuming the script
|
362
|
-
# contains a shebang. if not, the error should be obvious.
|
363
|
-
return partial_script_file_path
|
364
|
-
end
|
365
|
-
|
366
|
-
def format_executable_command(executable_file_path, *arguments)
|
367
|
-
escaped = []
|
368
|
-
[executable_file_path, arguments].flatten.each do |arg|
|
369
|
-
value = arg.to_s
|
370
|
-
needs_escape = value.index(" ") || value.index("\"") || value.index("'")
|
371
|
-
escaped << (needs_escape ? "\"#{value.gsub("\"", "\\\"")}\"" : value)
|
372
|
-
end
|
373
|
-
return escaped.join(" ")
|
374
|
-
end
|
375
|
-
|
376
|
-
def format_shell_command(shell_script_file_path, *arguments)
|
377
|
-
# shell files containing shebang are directly executable in linux, so
|
378
|
-
# assume our scripts have shebang. if not, the error should be obvious.
|
379
|
-
return format_executable_command(shell_script_file_path, arguments)
|
380
|
-
end
|
381
|
-
|
382
|
-
def format_redirect_stdout(cmd, target = NULL_OUTPUT_NAME)
|
383
|
-
return cmd + " 1>#{target}"
|
384
|
-
end
|
385
|
-
|
386
|
-
def format_redirect_stderr(cmd, target = NULL_OUTPUT_NAME)
|
387
|
-
return cmd + " 2>#{target}"
|
388
|
-
end
|
389
|
-
|
390
|
-
def format_redirect_both(cmd, target = NULL_OUTPUT_NAME)
|
391
|
-
return cmd + " 1>#{target} 2>&1"
|
392
|
-
end
|
393
|
-
|
394
|
-
def sandbox_ruby
|
395
|
-
"#{RightScale::Platform.filesystem.sandbox_dir}/bin/ruby"
|
396
|
-
end
|
397
|
-
|
398
|
-
# Gets the current system uptime.
|
399
|
-
#
|
400
|
-
# === Return
|
401
|
-
# the time the machine has been up in seconds, 0 if there was an error.
|
402
|
-
def uptime
|
403
|
-
return File.read('/proc/uptime').split(/\s+/)[0].to_f rescue 0.0
|
404
|
-
end
|
405
|
-
|
406
|
-
# Gets the time at which the system was booted
|
407
|
-
#
|
408
|
-
# === Return
|
409
|
-
# the UTC timestamp at which the system was booted
|
410
|
-
def booted_at
|
411
|
-
match = /btime ([0-9]+)/.match(File.read('/proc/stat')) rescue nil
|
412
|
-
|
413
|
-
if match && (match[1].to_i > 0)
|
414
|
-
return match[1].to_i
|
415
|
-
else
|
416
|
-
return nil
|
417
|
-
end
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
class Controller
|
422
|
-
# Shutdown machine now
|
423
|
-
def shutdown
|
424
|
-
`init 0`
|
425
|
-
end
|
426
|
-
|
427
|
-
# Reboot machine now
|
428
|
-
def reboot
|
429
|
-
`init 6`
|
430
|
-
end
|
431
|
-
end
|
432
|
-
|
433
|
-
class Rng
|
434
|
-
def pseudorandom_bytes(count)
|
435
|
-
f = File.open('/dev/urandom', 'r')
|
436
|
-
bytes = f.read(count)
|
437
|
-
f.close
|
438
|
-
|
439
|
-
bytes
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
class Process
|
444
|
-
# queries resident set size (current working set size in Windows).
|
445
|
-
#
|
446
|
-
# === Parameters
|
447
|
-
# pid(Fixnum):: process ID or nil for current process
|
448
|
-
#
|
449
|
-
# === Return
|
450
|
-
# result(Fixnum):: current set size in KB
|
451
|
-
def resident_set_size(pid=nil)
|
452
|
-
pid = $$ unless pid
|
453
|
-
return `ps -o rss= -p #{pid}`.to_i
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
class Installer
|
458
|
-
# The output generated by the Installer class
|
459
|
-
attr_accessor :output
|
460
|
-
|
461
|
-
@output = ""
|
462
|
-
|
463
|
-
class PackageNotFound < Exception; end
|
464
|
-
class PackageManagerNotFound < Exception; end
|
465
|
-
|
466
|
-
# Does this machine have aptitude
|
467
|
-
#
|
468
|
-
# === Return
|
469
|
-
# true:: If aptitude is available in the expected directory
|
470
|
-
# false:: Otherwise
|
471
|
-
def aptitude?
|
472
|
-
File.executable? '/usr/bin/apt-get'
|
473
|
-
end
|
474
|
-
|
475
|
-
# Does this machine have yum
|
476
|
-
#
|
477
|
-
# === Return
|
478
|
-
# true:: If yum is available in the expected directory
|
479
|
-
# false:: Otherwise
|
480
|
-
def yum?
|
481
|
-
File.executable? '/usr/bin/yum'
|
482
|
-
end
|
483
|
-
|
484
|
-
# Does this machine have zypper
|
485
|
-
#
|
486
|
-
# === Return
|
487
|
-
# true:: If zypper is available in the expected directory
|
488
|
-
# false:: Otherwise
|
489
|
-
def zypper?
|
490
|
-
File.executable? '/usr/bin/zypper'
|
491
|
-
end
|
492
|
-
|
493
|
-
# Install packages based on installed package manager
|
494
|
-
#
|
495
|
-
# === Parameters
|
496
|
-
# command_name(Array):: Array of packages names to be installed
|
497
|
-
#
|
498
|
-
# === Return
|
499
|
-
# true:: If installations of all packages was successfull
|
500
|
-
# false:: Otherwise
|
501
|
-
def install(packages)
|
502
|
-
return true if packages.empty?
|
503
|
-
|
504
|
-
packages = packages.uniq.join(' ')
|
505
|
-
failed_packages = []
|
506
|
-
|
507
|
-
if yum?
|
508
|
-
command = "yum install -y #{packages} 2>&1"
|
509
|
-
regex = /No package (.*) available\./
|
510
|
-
elsif aptitude?
|
511
|
-
ENV['DEBIAN_FRONTEND'] = "noninteractive"
|
512
|
-
command = "apt-get install -y #{packages} 2>&1"
|
513
|
-
regex = /E: Couldn't find package (.*)/
|
514
|
-
elsif zypper?
|
515
|
-
command = "zypper --no-gpg-checks -n #{packages} 2>&1"
|
516
|
-
regex = /Package '(.*)' not found\./
|
517
|
-
else
|
518
|
-
raise PackageManagerNotFound, "No package manager binary (apt, yum, zypper) found in /usr/bin"
|
519
|
-
end
|
520
|
-
|
521
|
-
@output = run_installer_command(command)
|
522
|
-
@output.scan(regex) { |package| failed_packages << package.first }
|
523
|
-
raise PackageNotFound, "The following packages were not available: #{failed_packages.join(', ')}" unless failed_packages.empty?
|
524
|
-
return true
|
525
|
-
end
|
526
|
-
|
527
|
-
protected
|
528
|
-
|
529
|
-
# A test hook so we can mock the invocation of the installer.
|
530
|
-
def run_installer_command(cmd)
|
531
|
-
`#{cmd}`
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
|
-
end # Platform
|
536
|
-
|
537
|
-
end # RightScale
|