cloudflock 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +15 -0
  2. data/bin/cloudflock +7 -1
  3. data/bin/cloudflock-files +2 -14
  4. data/bin/cloudflock-profile +3 -15
  5. data/bin/cloudflock-servers +3 -22
  6. data/bin/cloudflock.default +3 -22
  7. data/lib/cloudflock/app/common/cleanup/unix.rb +23 -0
  8. data/lib/cloudflock/app/common/cleanup.rb +107 -0
  9. data/lib/cloudflock/app/common/exclusions/unix/centos.rb +18 -0
  10. data/lib/cloudflock/app/common/exclusions/unix/redhat.rb +18 -0
  11. data/lib/cloudflock/app/common/exclusions/unix.rb +58 -0
  12. data/lib/cloudflock/app/common/exclusions.rb +57 -0
  13. data/lib/cloudflock/app/common/platform_action.rb +59 -0
  14. data/lib/cloudflock/app/common/rackspace.rb +63 -0
  15. data/lib/cloudflock/app/common/servers.rb +673 -0
  16. data/lib/cloudflock/app/files-migrate.rb +246 -0
  17. data/lib/cloudflock/app/server-migrate.rb +327 -0
  18. data/lib/cloudflock/app/server-profile.rb +130 -0
  19. data/lib/cloudflock/app.rb +87 -0
  20. data/lib/cloudflock/error.rb +6 -19
  21. data/lib/cloudflock/errstr.rb +31 -0
  22. data/lib/cloudflock/remote/files.rb +82 -22
  23. data/lib/cloudflock/remote/ssh.rb +234 -278
  24. data/lib/cloudflock/target/servers/platform.rb +92 -115
  25. data/lib/cloudflock/target/servers/profile.rb +331 -340
  26. data/lib/cloudflock/task/server-profile.rb +651 -0
  27. data/lib/cloudflock.rb +6 -8
  28. metadata +49 -68
  29. data/lib/cloudflock/interface/cli/app/common/servers.rb +0 -128
  30. data/lib/cloudflock/interface/cli/app/files.rb +0 -179
  31. data/lib/cloudflock/interface/cli/app/servers/migrate.rb +0 -491
  32. data/lib/cloudflock/interface/cli/app/servers/profile.rb +0 -88
  33. data/lib/cloudflock/interface/cli/app/servers.rb +0 -2
  34. data/lib/cloudflock/interface/cli/console.rb +0 -213
  35. data/lib/cloudflock/interface/cli/opts/servers.rb +0 -20
  36. data/lib/cloudflock/interface/cli/opts.rb +0 -87
  37. data/lib/cloudflock/interface/cli.rb +0 -15
  38. data/lib/cloudflock/target/servers/data/exceptions/base.txt +0 -44
  39. data/lib/cloudflock/target/servers/data/exceptions/platform/amazon.txt +0 -10
  40. data/lib/cloudflock/target/servers/data/exceptions/platform/centos.txt +0 -7
  41. data/lib/cloudflock/target/servers/data/exceptions/platform/debian.txt +0 -0
  42. data/lib/cloudflock/target/servers/data/exceptions/platform/redhat.txt +0 -7
  43. data/lib/cloudflock/target/servers/data/exceptions/platform/suse.txt +0 -1
  44. data/lib/cloudflock/target/servers/data/post-migration/chroot/base.txt +0 -1
  45. data/lib/cloudflock/target/servers/data/post-migration/chroot/platform/amazon.txt +0 -19
  46. data/lib/cloudflock/target/servers/data/post-migration/pre/base.txt +0 -3
  47. data/lib/cloudflock/target/servers/data/post-migration/pre/platform/amazon.txt +0 -4
  48. data/lib/cloudflock/target/servers/migrate.rb +0 -466
  49. data/lib/cloudflock/target/servers/platform/v1.rb +0 -97
  50. data/lib/cloudflock/target/servers/platform/v2.rb +0 -93
  51. data/lib/cloudflock/target/servers.rb +0 -5
  52. data/lib/cloudflock/version.rb +0 -3
@@ -1,394 +1,385 @@
1
1
  require 'cloudflock/remote/ssh'
2
+ require 'socket'
2
3
  require 'cpe'
3
4
 
4
- # Public: Provides methods to create a profile for a given host, mapping it to
5
- # available public cloud offerings, and reference data gathered during this
6
- # process.
7
- #
8
- # Examples
9
- #
10
- # # Generate a profile from a shell already logged in to a remote host
11
- # profile = Profile.new(ssh_object)
12
- # profile.build
13
- #
14
- # # Only determine the memory and I/O statistics for a host
15
- # profile = Profile.new(ssh_object)
16
- # profile.determine_memory
17
- # profile.determine_io
18
- class CloudFlock::Target::Servers::Profile
19
- # Public: Array containing warnings generated by the info gathering process.
20
- attr_reader :warnings
21
-
22
- # Public: Initialize the Profile object.
23
- #
24
- # shell - An SSH object open to the host to be profiled.
25
- #
26
- # Raises ArgumentError if passed anything but an SSH object.
27
- def initialize(shell)
28
- raise ArgumentError unless shell.kind_of? CloudFlock::Remote::SSH
29
-
30
- @shell = shell
31
-
32
- @warnings = []
33
- @info = {}
34
- end
35
-
36
- # Public: Run all available determinations against the SSH object.
37
- #
38
- # Returns nothing.
39
- def build
40
- determine_version
41
- determine_arch
42
- determine_hostname
43
- determine_memory
44
- determine_cpu
45
- determine_disk
46
- determine_ips
47
- determine_io
48
- determine_web
49
- determine_db
50
- determine_lib
51
- determine_rsync
52
- determine_processes
53
- end
54
-
55
- # Public: Determine vendor and version of the OS running on the target host,
56
- # and create an appropriate CPE object for it, assigning it to @info[:cpe].
57
- #
58
- # Returns nothing.
59
- def determine_version
60
- release = @shell.query("CPE", "cat /etc/system-release-cpe")
5
+ module CloudFlock; module Target; module Servers
6
+ class Profile
7
+ # Public: List of linux distributions supported by CloudFlock
8
+ SUPPORTED_DISTROS = %w{Arch CentOS Debian SUSE Ubuntu RedHat Gentoo}
9
+
10
+ # Public: Initialize the Profile object.
11
+ #
12
+ # shell - An SSH object which is open to the host which will be profiled.
13
+ #
14
+ # Raises TypeError if shell is not of type SSH.
15
+ def initialize(shell)
16
+ unless shell.is_a?(CloudFlock::Remote::SSH)
17
+ raise(TypeError, Errstr::NOT_SSH)
18
+ end
61
19
 
62
- begin
63
- cpe = CPE.parse(release)
64
- cpe.version.gsub!(/[^0-9.]/, '')
65
- @info[:cpe] = cpe
66
- return
67
- rescue ArgumentError
68
- cpe = CPE.new(part: CPE::OS, product: "linux")
20
+ @shell = shell
21
+ @warnings = []
22
+ @info = {}
69
23
  end
70
24
 
71
- issue = @shell.query("ISSUE", "cat /etc/issue")
72
-
73
- case issue
74
- when /Arch/
75
- cpe.vendor = "Arch"
76
- when /CentOS/
77
- cpe.vendor = "CentOS"
78
- issue.gsub!(/\.\d.*$/, '')
79
- when /Debian/
80
- cpe.vendor = "Debian"
81
- issue.gsub!(/\.\d.*$/, '')
82
- when /This is/
83
- cpe.vendor = "Gentoo"
84
- when /SUSE/
85
- cpe.vendor = "openSUSE"
86
- when /Ubuntu/
87
- cpe.vendor = "Ubuntu"
88
- when /Red/
89
- cpe.vendor = "Redhat"
90
- issue.gsub!(/\.\d.*$/, '')
91
- else
92
- cpe.vendor = "Unknown"
25
+ # Public: Build the profile by calling all methods which begin with
26
+ # 'determine_'.
27
+ #
28
+ # Returns nothing.
29
+ def build
30
+ methods.select { |x| x =~ /^determine_/ }.each do |method|
31
+ self.send(method)
32
+ end
33
+ methods.select { |x| x =~ /^warning_/ }.each do |method|
34
+ self.send(method)
35
+ end
93
36
  end
94
37
 
95
- cpe.version = version_number(issue)
96
-
97
- @info[:cpe] = cpe
98
- end
38
+ # Public: Allow access to the list of keys in @info.
39
+ #
40
+ # Returns an Array of keys in @info.
41
+ def keys
42
+ @info.keys
43
+ end
99
44
 
100
- # Public: Determine the architecture of the target host and assign it to
101
- # @info[:arch].
102
- #
103
- # Returns nothing.
104
- def determine_arch
105
- uname = @shell.query("UNAME", "uname -m")
106
- case uname
107
- when /x86_64/
108
- @info[:arch] = 'x86_64'
109
- when /i\d86/
110
- @info[:arch] = 'i386'
111
- else
112
- @info[:arch] = "Unknown"
45
+ # Public: Simplify access to @info.
46
+ #
47
+ # key - Object to be used as the key in the @info Hash.
48
+ #
49
+ # Returns a value stored in @info.
50
+ def [](key)
51
+ @info[key]
113
52
  end
114
- end
115
53
 
116
- # Public: Determine the hostname of the target host and assign it to
117
- # @info[:hostname]
118
- #
119
- # Returns nothing.
120
- def determine_hostname
121
- @info[:hostname] = @shell.query("HOST", "hostname")
122
- end
54
+ # Public: Return server information and warnings as a Hash.
55
+ #
56
+ # Returns a Hash.
57
+ def to_hash
58
+ @info.merge({warnings: @warnings})
59
+ end
123
60
 
124
- # Public: Determine the available and total memory on the target host as
125
- # well as historical usage via sar(1), if possible. Assign these to
126
- # @info[:memory] and @info[:memory_hist], respectively.
127
- #
128
- # Returns nothing.
129
- def determine_memory
130
- result = {}
131
-
132
- free = %w{free -m |awk '$1 ~ /Mem/ {print $2, $2-$6-$7}; $1 ~ /Swap/
133
- {print $3}'|xargs}.join(' ')
134
- mem = @shell.query("MEMORY", free)
135
- total, used, swap = mem.split(/\s+/)
136
-
137
- result[:total] = total.to_i
138
- result[:mem_used] = used.to_i
139
- result[:swap_used] = swap.to_i
140
- result[:swapping?] = swap.to_i > 0
141
- @info[:memory] = result
142
-
143
- # Determine average mem and swap usage
144
- result = {}
145
- sar_location = @shell.query("SAR", "which sar 2>/dev/null")
146
- sar_command = %w{for l in $(find /var/log/ -name 'sa??'); do sar -r -f $l |
147
- grep Average; done | awk '{I+=1; TOT=$2+$3; CACHE+=$5+$6;
148
- FREE+=$2; SWAP+=$9;} END {CACHE=CACHE/I; FREE=FREE/I;
149
- SWAP=SWAP/I; print (TOT-(CACHE+FREE))/TOT*100,
150
- SWAP;}'}.join(' ')
151
-
152
- if sar_location =~ /bin\//
153
- sar_usage = @shell.query("HIST_MEM", sar_command)
154
-
155
- if sar_usage =~ /\d \d/
156
- hist_mem, hist_swap = sar_usage.split(/ /)
157
- result[:mem_used] = hist_mem.to_i
158
- result[:swap_used] = hist_swap.to_i
61
+ private
62
+
63
+ # Internal: Determine important statistics relating to the CPU (available
64
+ # core count, speed).
65
+ #
66
+ # Returns nothing.
67
+ def determine_cpu
68
+ cpu = @info[:cpu] = {}
69
+
70
+ lscpu = @shell.query('LSCPU', 'lscpu')
71
+ if lscpu.empty?
72
+ cpuinfo = @shell.query('cat /proc/cpuinfo')
73
+ count = cpuinfo.lines.select { |l| l =~ /^processor\s*: [0-9]/}
74
+ speed = cpuinfo.lines.select { |l| l =~ /MHz/ }
75
+ cpu[:count] = count.size
76
+ cpu[:speed] = speed[0].to_s.gsub(/.* /, '')
77
+ else
78
+ cpu[:count] = lscpu.select { |l| l =~ /CPU\(s\)/ }.gsub(/.* /, '')
79
+ cpu[:speed] = lscpu.select { |l| l =~ /MHz/ }.gsub(/.* /, '')
159
80
  end
160
81
  end
161
82
 
162
- @info[:memory_hist] = result
163
- end
164
-
165
- # Public: Determine the number of CPUs present on the target host and the
166
- # speed of the processors. Assign these to @info[:cpu][:count] and
167
- # @info[:cpu][:speed] respectively.
168
- #
169
- # Returns nothing.
170
- def determine_cpu
171
- count_command = 'cat /proc/cpuinfo|grep "^processor\\s*: [0-9]"|wc -l'
172
- speed_command = 'cat /proc/cpuinfo|grep "MHz"|head -1'
173
- cpus = {}
174
- cpus[:count] = @shell.query("CPU", count_command).to_i
175
- cpus[:speed] = @shell.query("MHZ", speed_command).gsub(/.*: /, '').to_i
176
- @info[:cpu] = cpus
177
- end
178
-
179
- # Public: Determine the amount of disk space in use on the target host and
180
- # set that to @info[:disk].
181
- #
182
- # Returns nothing.
183
- def determine_disk
184
- # Use a less accurate (tends to inflate) method if du takes too long
185
- df_command = "df 2>/dev/null |awk '$1 ~ /\\// {I = I + $3} END {print I}'"
186
- disk = @shell.query("DISK_USED_DF", df_command)
187
-
188
- # Result is returned as KiB used. We need GB used.
189
- @info[:disk] = disk.to_f / 1000 ** 2
190
- end
191
-
192
- # Public: Determine the number of public and private IP addressess in use on
193
- # the target host and assign that to @info[:ip].
194
- #
195
- # Returns nothing.
196
- def determine_ips
197
- ips = {:private => [], :public => []}
83
+ # Internal: Determine the number and size of MySQL databases resident on
84
+ # the target host.
85
+ #
86
+ # Returns nothing.
87
+ def determine_databases
88
+ db = @info[:db] = {}
89
+ mysql_count_cmd = 'find /var/lib/mysql* -maxdepth 0 -type d ' \
90
+ '2>/dev/null|wc -l'
91
+ db[:count] = @shell.query('DB_MYSQL_COUNT', mysql_count_cmd)
92
+ db[:count] = db[:count].to_i
93
+
94
+ mysql_size_cmd = "du -s /var/lib/mysql 2>/dev/null|awk '{print $1}'"
95
+ db[:size] = @shell.query('DB_MYSQL_SIZE', mysql_size_cmd)
96
+ db[:size] = db[:size].to_i
97
+ end
198
98
 
199
- ip_command = %w{/sbin/ifconfig | grep 'inet addr' | egrep -v ':127' | sed
200
- -e 's/.*addr:\([0-9.]*\) .*/\1/'}.join(' ')
201
- ifconfig = @shell.query("IP_CONFIG", ip_command)
99
+ # Internal: Determine the amount of disk space in use on the target host.
100
+ #
101
+ # Returns nothing.
102
+ def determine_disk
103
+ df_cmd = "df 2>/dev/null|awk '$1 ~ /\\// {I=I+$3} END {print I}'"
104
+ disk = @shell.query('DISK_DF', df_cmd)
202
105
 
203
- ifconfig.each_line do |ip|
204
- ip.strip!
205
- ips[rfc1918?(ip)] << ip
106
+ # Result is expected to be in KiB. Convert to GB.
107
+ @info[:disk] = disk.to_f / 1000 ** 2
206
108
  end
207
109
 
208
- @info[:ip] = ips
209
- end
110
+ # Internal: Attempt to determine which linux distribution the target host
111
+ # is running.
112
+ #
113
+ # Returns nothing.
114
+ def determine_distribution
115
+ # Some distros ship with a file containing the CPE for their platform;
116
+ # this should be used if at all possible.
117
+ release = @shell.query('CPE', 'cat /etc/system-release-cpe')
118
+ begin
119
+ cpe = CPE.parse(release)
120
+ cpe.version.gsub!(/[^0-9.]/, '')
121
+ @info[:cpe] = cpe
122
+ return
123
+ rescue ArgumentError
124
+ cpe = CPE.new(part: CPE::OS, product: 'linux')
125
+ end
210
126
 
211
- # Public: Determine amount of historical I/O usage via sysstat and set it to
212
- # @info[:io].
213
- #
214
- # Returns nothing.
215
- def determine_io
216
- io = {}
127
+ # Fall back to depending on /etc/issue if available
128
+ issue = @shell.query('ISSUE', 'cat /etc/issue')
129
+ cpe.vendor = distro_name(issue)
217
130
 
218
- iostat = @shell.query("IOSTAT", "iostat -c | sed -n 4p | awk '{print $4}'")
219
- io[:wait] = iostat.to_f
131
+ # If all else fails, resort to looking in release files
132
+ if cpe.vendor.empty?
133
+ release_cmd = "grep -h '^ID=' /etc/[A-Za-z]*[_-][rv]e[lr]*|head -1"
134
+ release = @shell.query("RELEASE", release_cmd)
135
+ cpe.vendor = distro_name(release)
136
+ end
220
137
 
221
- up = @shell.query("UPTIME", "uptime | sed -e 's/.*up\\([^,]*\\),.*/\\1/'")
222
- io[:uptime] = up.chomp
138
+ # Fall back to "Unknown"
139
+ cpe.vendor = "" if cpe.vendor.empty?
223
140
 
224
- @info[:io] = io
225
- end
141
+ # Version number will be determined from /etc/issue
142
+ cpe.version = version_number(issue)
143
+ @info[:cpe] = cpe
144
+ end
226
145
 
227
- # Public: Determine the web server used on the host, and attempt to enumerate
228
- # domain names configured on the server for both non-SSL and SSL; set this
229
- # information to @info[:web]. Presently only Apache is supported.
230
- #
231
- # Returns nothing.
232
- def determine_web
233
- web = {}
234
-
235
- netstat_command = %w{netstat -ntlp | awk '$4 ~ /:80$/ || $4 ~ /:443$/
236
- {sub(/^[^\/]*\//, ""); print $NF}' | head -1}.join(' ')
237
- web[:binary] = @shell.query("WEB_NETSTAT", netstat_command)
238
-
239
- unless web[:binary].empty?
240
- version_command = "`which #{web[:binary]}` -v | grep version"
241
- web[:version] = @shell.query("WEB_VERSION", version_command)
242
- web[:version].gsub!(/.*version: /i, '')
243
- binary = web[:binary] == "httpd" ? "apachectl" : "apache2ctl"
244
- binary << " -S 2>&1"
245
-
246
- web_command = "#{binary} | grep ')' | grep -vi 'default' | wc -l"
247
- hosts = @shell.query("WEB_HTTP", web_command)
248
- web[:hosts_http] = hosts.to_i
249
-
250
- ssl_command = "#{binary} | grep ':443'|grep -vi 'default' | wc -l"
251
- ssl_hosts = @shell.query("WEB_HTTPS", ssl_command)
252
- web[:hosts_https] = ssl_hosts.to_i
146
+ # Internal: Determine the hostname of the target host.
147
+ #
148
+ # Returns nothing.
149
+ def determine_hostname
150
+ @info[:hostname] = @shell.query('HOST', 'hostname')
253
151
  end
254
152
 
255
- @info[:web] = web
256
- end
153
+ # Internal: Determine the amount of historical IO activity on the target
154
+ # host using sysstat if available.
155
+ #
156
+ # Returns nothing.
157
+ def determine_io
158
+ io = @info[:io] = {}
257
159
 
258
- # Public: Determine the amount of disk usage attributable to databases, as
259
- # well as database count, and set this to @info[:database]. Currently
260
- # supports MySQL.
261
- #
262
- # Returns nothing.
263
- def determine_db
264
- db = {}
265
- mysql_count = %w{find /var/lib/mysql/* -maxdepth 0 -type d 2>/dev/null |
266
- wc -l}.join(' ')
267
- db[:count] = @shell.query("DB_MYSQL_COUNT", mysql_count)
160
+ iostat = @shell.query('IOSTAT', "iostat -c|sed -n 4p|awk '#{print $4}'")
161
+ io[:wait] = iostat.to_f
268
162
 
269
- mysql_size = "du -s /var/lib/mysql 2>/dev/null | awk '{print $1}'"
270
- db[:size] = @shell.query("DB_MYSQL_SIZE", mysql_size)
163
+ up = @shell.query('UPTIME', "uptime|sed -e 's/.*up\\([^,]*\\),.*/\\1/'")
164
+ io[:uptime] = up.chomp
165
+ end
271
166
 
272
- db[:count] = db[:count].to_i
273
- db[:size] = db[:size].to_i
167
+ # Internal: Determine IPv4 addresses in use by the target host, splitting
168
+ # them into public and private groups.
169
+ #
170
+ # Returns nothing.
171
+ def determine_ips
172
+ ips = @info[:ip] = {private: [], public: []}
274
173
 
275
- @info[:db] = db
276
- end
174
+ ifc_cmd = "/sbin/ifconfig|grep 'inet addr'|grep -v ':127'|sed -e " \
175
+ "'s/.*addr:\([0-9.]*\) .*/\\1/'"
176
+ ifconfig = @shell.query('IFCONFIG', ifc_cmd)
277
177
 
278
- # Public: Gather information about the currently installed libc, ruby, perl,
279
- # python and php versions on the host, then set these values to @info[:lib].
280
- #
281
- # Returns nothing.
282
- def determine_lib
283
- lib = {}
178
+ ifconfig.each_line do |ip|
179
+ ip.strip!
180
+ ips[rfc1918?(ip)] << ip
181
+ end
182
+ end
284
183
 
285
- libc_command = %w{ls -al `find /lib /usr/lib -name 'libc.so*' | head -1` |
286
- sed 's/.*-> //'}.join(' ')
287
- lib[:libc] = @shell.query("LIBC", libc_command)
288
- lib[:libc].gsub!(/^.*-|\.so$/, '')
184
+ # Internal: Determine common libraries installed on the system.
185
+ #
186
+ # Returns nothing.
187
+ def determine_libraries
188
+ lib = @info[:lib] = {}
289
189
 
290
- lib[:perl] = @shell.query("PERL", "perl -e 'print $^V;'")
291
- lib[:perl] = lib[:perl].gsub(/^v([0-9.]*).*/, '\1')
190
+ libc_cmd = "ls -la `find /lib /usr/lib -name 'libc.so*'|head -1`|" \
191
+ "sed 's/.*-> //'"
192
+ lib[:libc] = @shell.query('LIBC', libc_cmd)
193
+ lib[:libc].gsub!(/^.*-|\.so$/, '')
292
194
 
293
- python_command = "python -c 'import sys; print sys.version' 2>/dev/null"
294
- lib[:python] = @shell.query("PYTHON", python_command)
295
- lib[:python] = lib[:python].gsub(/^([0-9.]*).*/m, '\1')
195
+ lib[:perl] = @shell.query('PERL', 'perl -e "print $^V;"')
196
+ lib[:perl].gsub!(/^v([0-9.]*).*/, '\1')
296
197
 
297
- ruby_command = "ruby -e 'print RUBY_VERSION' 2>/dev/null"
298
- lib[:ruby] = @shell.query("RUBY", ruby_command)
198
+ python_cmd = 'python -c "import sys; print sys.version" 2>/dev/null'
199
+ lib[:python] = @shell.query('PYTHON', python_cmd)
200
+ lib[:python].gsub!(/([0-9.]*).*/m, '\1')
299
201
 
300
- lib[:php] = @shell.query("PHP_VER", "php -v 2>/dev/null | head -1")
301
- lib[:php] = lib[:php].gsub(/^PHP ([0-9.]*).*/, '\1')
202
+ ruby_cmd = 'ruby -e "print RUBY_VERSION" 2>/dev/null'
203
+ lib[:ruby] = @shell.query('RUBY', ruby_cmd)
302
204
 
303
- @info[:lib] = lib
304
- end
205
+ lib[:php] = @shell.query('PHP', 'php -v 2>/dev/null|head -1')
206
+ lib[:php].gsub!(/^PHP ([0-9.]*).*/, '\1')
207
+ end
305
208
 
306
- # Public: Check for the existence of rsync(1) on the host. Set @info[:rsync]
307
- # accordingly.
308
- #
309
- # Returns nothing.
310
- def determine_rsync
311
- rsync_command = %w{which rsync 2>/dev/null || ([ -f
312
- /root/.rackspace/rsync ] && printf
313
- '/root/.rackspace/rsync') || printf
314
- 'NONE'}.join(' ')
209
+ # Internal: Determine the total amount of memory on the target host, the
210
+ # amount of memory in use, and the amount of swap space being used.
211
+ #
212
+ # Returns nothing.
213
+ def determine_memory
214
+ result = @info[:memory] = {}
215
+
216
+ free_cmd = "free -m|awk '$1 ~ /Mem/ {print $2, $2-$6-$7}; $1 ~ /Swap/ " \
217
+ "{print $3}'|xargs"
218
+ mem = @shell.query('MEMORY', free_cmd)
219
+ total, used, swap = mem.split(/\s+/)
220
+
221
+ result[:total] = total.to_i
222
+ result[:mem_used] = used.to_i
223
+ result[:swap_used] = swap.to_i
224
+ result[:swapping?] = swqp.to_i > 0
225
+ end
315
226
 
316
- rsync = @shell.query("RSYNC", rsync_command)
227
+ # Internal: If the sysstat suite is installed on the target host, determine
228
+ # the average amount of memory and swap used over whatever historical
229
+ # period sar is able to represent.
230
+ #
231
+ # Returns nothing.
232
+ def determine_memory_history
233
+ result = @info[:memory_hist] = {}
234
+ sar_cmd = "for l in $(find /var/log -name 'sa??');do sar -r -f $l|" \
235
+ "grep Average;done|awk '{I+=1;TOT=$2+$3;CACHE+=$5+$6;" \
236
+ "FREE+=$2;SWAP+=$9;} END {CACHE=CACHE/I;FREE=FREE/I;" \
237
+ "SWAP=SWAP/I;print (TOT-(CACHE+FREE))/TOT*100,SWAP;}'"
238
+
239
+ sar_location = @shell.query('SAR_LOCATION', 'which sar 2>/dev/null')
240
+ if sar_location =~ /bin\//
241
+ sar_usage = @shell.query('SAR', sar_cmd)
242
+
243
+ if sar_usage =~/\d \d/
244
+ mem, swap = sar_usage.split(/ /)
245
+ result[:mem_used] = mem
246
+ result[:swap_used] = swap
247
+ end
248
+ end
249
+ end
317
250
 
318
- @info[:rsync] = (rsync =~ /NONE/).nil? ? rsync : false
319
- end
251
+ # Internal: Gather a list of all listening ports on the target host.
252
+ #
253
+ # Returns nothing.
254
+ def determine_ports
255
+ ports = @info[:ports] = {}
256
+
257
+ netstat = @shell.query('NETSTAT', "netstat -ntlp|awk '{print $4, $NF}'")
258
+ netstat.lines.each do |line|
259
+ net, process = line.split(/\s+/, 2)
260
+ process = process.split(/\//, 2)[1]
261
+ net = net.gsub(/([0-9.:]+):([0-9]+)/, '\1 \2')
262
+ net, port = net.split(/ /, 2)
263
+
264
+ ports[net] ||= {}
265
+ ports[net][port] = process
266
+ end
267
+ end
320
268
 
321
- # Public: Check the process listing and store it in
322
- #
323
- # Returns nothing.
324
- def determine_processes
325
- ps_command = 'ps aux'
269
+ # Internal: Gather a list of running processes on the target host.
270
+ #
271
+ # Returns nothing.
272
+ def determine_processes
273
+ procs = @shell.query('PROCESSES', 'ps aux')
274
+ @info[:processes] = procs.gsub(/\r/, '').split(/\n/)
275
+ end
326
276
 
327
- processes = @shell.query("PS_LIST", ps_command)
328
- @info[:processes] = processes.gsub(/\r/, '').split(/\n/)
277
+ # Internal: Locate rsync on the target host.
278
+ #
279
+ # Returns nothing.
280
+ def determine_rsync
281
+ rsync = @shell.query('RSYNC', 'which rsync 2>/dev/null')
282
+
283
+ if rsync.empty?
284
+ rsync_cmd = '[ -f /root/.cloudflock/rsync ] && printf ' \
285
+ '"/root/.rackspace/rsync"'
286
+ rsync = @shell.query('LOCAL_RSYNC', rsync_cmd)
287
+ rsync = nil if rsync.empty?
288
+ end
329
289
 
330
- unless @info[:processes].grep(/psa/i).empty?
331
- @warnings << "Server likely to be running Plesk"
290
+ @info[:rsync] = rsync
332
291
  end
333
- unless @info[:processes].grep(/cpanellogd/i).empty?
334
- @warnings << "Server likely to be running cPanel"
292
+
293
+ # Internal: Determine the architecture of the target host.
294
+ #
295
+ # Returns nothing.
296
+ def determine_system_architecture
297
+ @info[:arch] = @shell.query('UNAME', 'uname -m')
298
+ @info[:arch].gsub!(/i\d86/, 'i386')
335
299
  end
336
- end
337
300
 
338
- # Public: Simplify access to @info.
339
- #
340
- # key - Key to check in @info.
341
- #
342
- # Return value contained in @info by key.
343
- def [](key)
344
- @info[key]
345
- end
301
+ # Internal: Determine which web server, if any, is running on the target
302
+ # host. If the web server is supported, discover how many HTTP/HTTPS
303
+ # domains are configured on the server.
304
+ #
305
+ # Returns nothing.
306
+ def determine_web
307
+ web = @info[:web] = {}
308
+ netstat_cmd = 'netstat -ntlp|awk \'$4 ~ /:80$/ || $4 ~ /:443$/ ' \
309
+ '{sub (/^[^\/]*\//, ""); print $NF}\'|head -1'
310
+ web[:binary] = @shell.query('WEB_NETSTAT', netstat_command)
311
+
312
+ unless web[:binary].empty?
313
+ if web[:binary] == 'httpd' || web[:binary] == 'apache2'
314
+ version_cmd = "`which #{web[:binary]}` -v|grep version"
315
+ web[:version] = @shell.query('WEB_VERSION', version_cmd)
316
+ web[:version].gsub!(/.*version: /i, '')
317
+
318
+ ctl_cmd = web[:binary] == 'httpd' ? 'apachectl' : 'apache2ctl'
319
+ ctl_cmd << ' -S 2>&1'
320
+
321
+ web_cmd = "#{ctl_cmd}|grep -vi 'default'|wc -l"
322
+ hosts = @shell.query('WEB_HOSTS', web_cmd)
323
+
324
+ ssl = hosts.lines.select { |line| line =~ /:443([^\d]|$)/ }
325
+ http = hosts.lines - ssl
326
+ web[:hosts_http] = http.length
327
+ web[:hosts_https] = https.length
328
+ end
329
+ end
330
+ end
346
331
 
347
- # Public: Allow access to the list of keys extant in @info.
348
- #
349
- # Return an Array of keys present in @info.
350
- def keys
351
- @info.keys
352
- end
332
+ # Internal: Check for signs of running Plesk.
333
+ #
334
+ # Returns nothing.
335
+ def warning_plesk
336
+ unless @info[:processes].to_a.grep(/psa/i).empty?
337
+ @warnings << "Server likely to be running Plesk"
338
+ end
339
+ end
353
340
 
354
- # Public: Return server information and warnings as a Hash. Useful for
355
- # calling Hash#merge.
356
- #
357
- # Return info Hash.
358
- def to_hash
359
- @info.merge({warnings: @warnings})
360
- end
341
+ # Internal: Check for signs of running cPanel.
342
+ #
343
+ # Returns nothing.
344
+ def warning_webmin
345
+ unless @info[:processes].to_a.grep(/cpanel/i).empty?
346
+ @warnings << "Server likely to be running cPanel"
347
+ end
348
+ end
361
349
 
362
- # Internal: Deconstruct the version String provided in order to strip out
363
- # extraneous data before and after the version number. Version number
364
- # strings are defined as beginning with a digit, ending with a digit, and
365
- # containing nothing but digits and at most one decimal point.
366
- #
367
- # version - A String containing version number of the OS on the target host.
368
- #
369
- # Returns a String containing the parsed version string.
370
- def version_number(version)
371
- if version =~ /\d/
372
- version.gsub(/^[^\d]*/, '').gsub(/[^\d]*$/, '').gsub(/(\d*\.\d*).*/, '\1')
373
- else
374
- "-"
350
+ # Internal: Search for names of supported Linux distributions in a string
351
+ # which may contain the name of the distribution currently installed on
352
+ # the target host.
353
+ #
354
+ # Returns a String.
355
+ def distro_name(str)
356
+ SUPPORTED_DISTROS.select do |distro|
357
+ Regexp.new(distro, Regexp::IGNORECASE).match(str)
358
+ end[0].to_s
375
359
  end
376
- end
377
360
 
378
- # Internal: Determine whether a given IP resides in a block designated as
379
- # private by RFC1918.
380
- #
381
- # ip - A String containing an IP address.
382
- #
383
- # Returns :private or :public Symbol based on whether the IP is private.
384
- def rfc1918?(ip)
385
- octets = ip.split /\./
386
- if octets[0] == "10" || (octets[0] == "192" && octets[1] == "168")
387
- return :private
388
- elsif octets[0] == "172" && octets[1].to_i >=16 && octets[1].to_i <= 31
389
- return :private
361
+ # Internal: Inspect a String which may contain information regarding the
362
+ # version of the Linux distribution running on the target host.
363
+ #
364
+ # Returns nothing.
365
+ def version_number(str)
366
+ if str =~ /\d/
367
+ str.gsub(/^[^\d]*/, '').gsub(/[^\d]*$/, '').gsub(/(\d*\.\d*).*/, '\1')
368
+ else
369
+ '-'
370
+ end
390
371
  end
391
372
 
392
- :public
373
+ # Internal: Determine if a v4 IP address belongs to a private (RFC 1918)
374
+ # network.
375
+ #
376
+ # ip - String containing an IP.
377
+ #
378
+ # Returns either the symbol :public or :private.
379
+ def rfc1918?(ip)
380
+ return :private if Addrinfo.ip(ip).ipv4_private?
381
+
382
+ :public
383
+ end
393
384
  end
394
- end
385
+ end; end; end