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