cloudflock 0.6.1 → 0.7.0

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.
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
@@ -0,0 +1,651 @@
1
+ require 'cloudflock/remote/ssh'
2
+ require 'cpe'
3
+
4
+ module CloudFlock; module Task
5
+ class ServerProfile
6
+ # Public: List of linux distributions supported by CloudFlock
7
+ DISTRO_NAMES = %w{Arch CentOS Debian Gentoo Scientific SUSE Ubuntu RedHat}
8
+
9
+ # Internal: Sections of the profile.
10
+ Section = Struct.new(:title, :entries)
11
+
12
+ # Internal: Individual entries for profiled data.
13
+ Entry = Struct.new(:name, :values)
14
+
15
+ attr_reader :cpe
16
+ attr_reader :warnings
17
+ attr_reader :process_list
18
+
19
+ # Public: Initialize the Profile object.
20
+ #
21
+ # shell - An SSH object which is open to the host which will be profiled.
22
+ def initialize(shell)
23
+ @shell = shell
24
+ @cpe = nil
25
+ @warnings = []
26
+ @info = []
27
+
28
+ build
29
+ end
30
+
31
+ # Public: Return server information and warnings as a Hash.
32
+ #
33
+ # Returns a Hash.
34
+ def to_hash
35
+ { info: @info, warnings: @warnings }
36
+ end
37
+
38
+ # Public: Select from the info Array, passing Section titles to the block
39
+ # provided and returning a list of entries contained within matching
40
+ # sections.
41
+ #
42
+ # Examples
43
+ #
44
+ # profile.select { |title| title == 'Memory Statistics' }
45
+ # # => [...]
46
+ #
47
+ # profile.select { |title| /Memory/.match title }
48
+ # # => [...]
49
+ #
50
+ # Yields titles of Section structs (Strings).
51
+ #
52
+ # Returns an Array of Entry structs.
53
+ def select(&block)
54
+ sections = @info.select { |section| block.call(section.title) }
55
+ sections.map! { |section| section.entries }
56
+ sections.flatten
57
+ end
58
+
59
+ # Public: Select values from within entries, specifying both section and
60
+ # entry names.
61
+ #
62
+ # section - String or Regexp specifying the section name.
63
+ # name - String or Regexp specifying the desired entry's name.
64
+ #
65
+ # Examples
66
+ #
67
+ # profile.select_entries('Memory Statistics', 'Used RAM')
68
+ # # => [...]
69
+ #
70
+ # profile.select_entries(/Memory/, 'Used RAM')
71
+ # # => [...]
72
+ #
73
+ # Returns an Array of Strings.
74
+ def select_entries(section, name)
75
+ entries = select { |header| header.match section }
76
+ filtered = entries.select { |entry| name.match entry.name }
77
+ filtered.map(&:values)
78
+ end
79
+
80
+ private
81
+
82
+ # Internal: Build the profile by calling all methods which begin with
83
+ # 'build_' and 'warning_'.
84
+ #
85
+ # Returns nothing.
86
+ def build
87
+ private_methods.select { |x| x =~ /^build_/ }.each do |method|
88
+ @info.push self.send(method)
89
+ end
90
+ private_methods.select { |x| x =~ /^warning_/ }.each do |method|
91
+ self.send(method)
92
+ end
93
+ end
94
+
95
+ # Internal: Filter methods by name, creating a new Section struct in which
96
+ # to hold results.
97
+ #
98
+ # name - Name to give to the new Section struct.
99
+ # method_filter - Regexp against which to match method names.
100
+ #
101
+ # Examples
102
+ #
103
+ # filter_tasks('System Information', /^determine_system_/)
104
+ #
105
+ # Returns a Section struct.
106
+ def filter_tasks(name, method_filter)
107
+ section = Section.new(name)
108
+ tasks = private_methods.select { |task| method_filter.match(task) }
109
+ section.entries = tasks.map do |method|
110
+ self.send(method)
111
+ end
112
+
113
+ section
114
+ end
115
+
116
+ # Internal: Build the "System Information" Entries.
117
+ #
118
+ # Returns nothing.
119
+ def build_system
120
+ filter_tasks('System Information', /^determine_system_/)
121
+ end
122
+
123
+ # Internal: Build the "CPU Statistics" Entries.
124
+ #
125
+ # Returns nothing.
126
+ def build_cpu
127
+ filter_tasks('CPU Statistics', /^determine_cpu_/)
128
+ end
129
+
130
+ # Internal: Build the "Memory Statistics" Entries.
131
+ #
132
+ # Returns nothing.
133
+ def build_memory
134
+ filter_tasks('Memory Statistics', /^determine_memory_/)
135
+ end
136
+
137
+ # Internal: Build the "System Usage" Entries.
138
+ #
139
+ # Returns nothing.
140
+ def build_load
141
+ filter_tasks('System Usage', /^determine_load_/)
142
+ end
143
+
144
+ # Internal: Build the "Storage Statistics" Entries.
145
+ #
146
+ # Returns nothing.
147
+ def build_storage
148
+ filter_tasks('Storage Statistics', /^determine_storage_/)
149
+ end
150
+
151
+ # Internal: Build the "IP Usage" Entries.
152
+ #
153
+ # Returns nothing.
154
+ def build_network
155
+ filter_tasks('IP Usage', /^determine_network_/)
156
+ end
157
+
158
+ # Internal: Build the "Installed Libraries" Entries.
159
+ #
160
+ # Returns nothing.
161
+ def build_library
162
+ filter_tasks('Installed Libraries', /^determine_library_/)
163
+ end
164
+
165
+ # Internal: Build the "System Services" Entries.
166
+ #
167
+ # Returns nothing.
168
+ def build_services
169
+ filter_tasks('System Services', /^determine_services_/)
170
+ end
171
+
172
+ # Internal: Attempt to determine which linux distribution the target host
173
+ # is running.
174
+ #
175
+ # Returns an Entry struct.
176
+ def determine_system_distribution
177
+ set_system_cpe
178
+ vendor = cpe.vendor
179
+ product = cpe.product.gsub(/_/, ' ').capitalize
180
+ version = cpe.version
181
+ platform = [vendor, product, version].join(' ')
182
+
183
+ warn("Unable to determine the target host's platform") if vendor.empty?
184
+
185
+ Entry.new('OS', platform + " (#{@cpe.to_s})")
186
+ end
187
+
188
+ # Internal: Determine the architecture of the target host.
189
+ #
190
+ # Returns an Entry struct.
191
+ def determine_system_architecture
192
+ arch = query('uname -m').gsub(/i\d86/, 'x86')
193
+
194
+ warn("Unable to determine target architecture") if arch.empty?
195
+
196
+ Entry.new('Arch', arch)
197
+ end
198
+
199
+ # Internal: Determine the hostname of the target host.
200
+ #
201
+ # Returns an Entry struct.
202
+ def determine_system_hostname
203
+ hostname = query('hostname')
204
+ Entry.new('Hostname', hostname)
205
+ end
206
+
207
+ # Internal: Gather a list of running processes on the target host.
208
+ #
209
+ # Returns an Entry struct.
210
+ def determine_system_process_list
211
+ procs = query('ps aux')
212
+ @process_list = procs.lines
213
+
214
+ Entry.new('Process Count', procs.lines.to_a.length)
215
+ end
216
+
217
+ # Internal: Determine the number of CPU cores on the target host.
218
+ #
219
+ # Returns an Entry struct.
220
+ def determine_cpu_count
221
+ lscpu = query('lscpu')
222
+
223
+ # If lscpu is available, it gives the best information. Otherwise, fall
224
+ # back to sysctl which should handle the vast majority of Unix systems.
225
+ if /CPUs?/.match(lscpu)
226
+ count = lscpu.lines.select { |l| l =~ /CPU\(s\)/ }[0].gsub(/.* /, '')
227
+ count = count
228
+ else
229
+ # hw.ncpu covers BSD hosts (the primary case when lscpu(1) is not
230
+ # present on a system). kernel.sched_domain covers linux hosts which
231
+ # do not have have lscpu installed.
232
+ #
233
+ # Example expected outputs on a 2-core smp system:
234
+ # $ sysctl hw.ncpu
235
+ # hw.ncpu: 2
236
+ #
237
+ # $ sysctl kernel.sched_domain
238
+ # kernel.sched_domain.cpu0.domain0.busy_factor = 64
239
+ # ...
240
+ # kernel.sched_domain.cpu1.domain1.wake_idx = 0
241
+ sysctl = query('sysctl hw.ncpu || sysctl kernel.sched_domain')
242
+ if /hw\.ncpu: /.match sysctl
243
+ count = sysctl.gsub(/.*(\d)/, '\\1').to_i
244
+ else
245
+ sysctl = sysctl.lines.select { |line| /cpu\.?\d+/.match(line) }
246
+ sysctl.map! { |line| line.gsub(/.*(cpu).*?(\d*).*/m, '\\1\\2') }
247
+ count = sysctl.uniq.length
248
+ end
249
+ end
250
+
251
+ warn("Unable to determine target CPU count") if count.to_i < 1
252
+
253
+ Entry.new('CPU Count', count.to_i)
254
+ end
255
+
256
+ # Internal: Determine the CPU model on the target host.
257
+ #
258
+ # Returns an Entry struct.
259
+ def determine_cpu_model
260
+ lscpu = query('lscpu').lines.select { |l| l =~ /^model name/i }
261
+ if lscpu.empty?
262
+ cpuinfo = query('cat /proc/cpuinfo')
263
+ model = cpuinfo.lines.select { |l| l =~ /model name/i }
264
+ model = model[0].to_s.strip.gsub(/.*: */, '')
265
+ else
266
+ model = lscpu[0].strip.gsub(/.* /, '')
267
+ end
268
+
269
+ warn("Unable to determine target CPU model") if model.empty?
270
+
271
+ Entry.new('Processor Model', model)
272
+ end
273
+
274
+ # Internal: Determine the total amount of memory on the target host.
275
+ #
276
+ # Returns an Entry struct.
277
+ def determine_memory_total
278
+ mem = query('free -m')
279
+ if /^Mem/.match(mem)
280
+ total = mem.lines.select { |l| l =~ /^Mem/ }.first.split(/\s+/)[1]
281
+ else
282
+ total = 0
283
+ warn('Unable to determine target Memory')
284
+ end
285
+ Entry.new('Total RAM', "#{total} MiB")
286
+ end
287
+
288
+ # Internal: Determine the total amount of wired memory on the target host.
289
+ #
290
+ # Returns an Entry struct.
291
+ def determine_memory_wired
292
+ mem = query('free -m')
293
+ if /^Mem/.match(mem)
294
+ mem = mem.lines.select { |l| l =~ /^Mem/ }.first
295
+ mem = mem.split(/\s+/).map(&:to_i)
296
+ total = mem[1]
297
+ used = total - mem[3..-1].reduce(&:+)
298
+ used = sprintf("%#{total.to_s.length}d", used)
299
+ percent = ((used.to_f / total) * 100).to_i
300
+ else
301
+ used = 0
302
+ percent = 0
303
+ end
304
+
305
+ Entry.new('Used RAM', "#{used} MiB (#{percent}%)")
306
+ end
307
+
308
+ # Internal: Determine the total amount of swap used on the target host.
309
+ #
310
+ # Returns an Entry struct.
311
+ def determine_memory_swap
312
+ mem = query('free -m')
313
+ if /^Swap/.match(mem)
314
+ mem = mem.lines.select { |l| l =~ /^Swap/ }.first
315
+ total, used = mem.split(/\s+/).map(&:to_i)[1..2]
316
+ used = sprintf("%#{total.to_s.length}d", used)
317
+ percent = ((used.to_f / total) * 100).to_i
318
+
319
+ warn('Host is swapping') if percent > 0
320
+ else
321
+ used = percent = 0
322
+ warn('Unable to enumerate swap')
323
+ end
324
+
325
+ Entry.new('Used Swap', "#{used} MiB (#{percent}%)")
326
+ rescue ZeroDivisionError, FloatDomainError
327
+ Entry.new('Used Swap', 'No swap configured')
328
+ end
329
+
330
+ # Internal: If the sysstat suite is installed on the target host, determine
331
+ # the average amount of memory used over whatever historical period sar is
332
+ # able to represent.
333
+ #
334
+ # Returns an Entry struct.
335
+ def determine_memory_usage_historical_average
336
+ sar_location = query('which sar')
337
+ usage = ''
338
+
339
+ if sar_location =~ /bin\//
340
+ sar_cmd = "for l in $(find /var/log -name 'sa??');do sar -r -f $l|" \
341
+ "grep Average;done|awk '{I+=1;TOT=$2+$3;CACHE+=$5+$6;" \
342
+ "FREE+=$2;} END {CACHE=CACHE/I;FREE=FREE/I;" \
343
+ "print (TOT-(CACHE+FREE))/TOT*100;}'"
344
+
345
+ usage = query(sar_cmd)
346
+ end
347
+ usage = '' unless usage =~ /\d/
348
+
349
+ warn('No historical usage information available') if usage.empty?
350
+
351
+ Entry.new('Average Used', usage)
352
+ end
353
+
354
+ # Internal: If the sysstat suite is installed on the target host, determine
355
+ # the average amount of swap used over whatever historical period sar is
356
+ # able to represent.
357
+ #
358
+ # Returns an Entry struct.
359
+ def determine_memory_swap_historical_average
360
+ sar_location = query('which sar 2>/dev/null')
361
+ usage = ''
362
+
363
+ if sar_location =~ /bin\//
364
+ sar_cmd = "for l in $(find /var/log -name 'sa??');do sar -r -f $l|" \
365
+ "grep Average;done|awk '{I+=1;;SWAP+=$9;} END " \
366
+ "{SWAP=SWAP/I;print SWAP;}'"
367
+
368
+ usage = query(sar_cmd)
369
+ end
370
+ usage = '' unless usage =~ /\d/
371
+
372
+ Entry.new('Average Swap', usage)
373
+ end
374
+
375
+ # Internal: Determine the amount of time the target host has been running.
376
+ #
377
+ # Returns an Entry struct.
378
+ def determine_load_uptime
379
+ up = query('uptime')
380
+ up.gsub!(/.* up([^,]*),.*/, '\\1')
381
+
382
+ Entry.new('Uptime', up.strip)
383
+ end
384
+
385
+ # Internal: Determine the load averages on the target host.
386
+ #
387
+ # Returns an Entry struct.
388
+ def determine_load_average
389
+ avg = query('uptime')
390
+ avg.gsub!(/^.* load averages?: |,.*$/i, '')
391
+
392
+ warn('System is under heavy load') if avg.to_i > 10
393
+
394
+ Entry.new('Load Average', avg)
395
+ end
396
+
397
+ # Internal: If the sysstat suite is installed on the target host, determine
398
+ # the amount of historical IO activity on the target host.
399
+ #
400
+ # Returns an Entry struct.
401
+ def determine_load_iowait
402
+ iostat = query('iostat').lines.to_a[3].to_s.strip.split(/\s+/)[3]
403
+ wait = iostat.to_f
404
+
405
+ warn('Cannot determine IO Wait') if iostat.to_s.empty?
406
+ warn('IO Wait is high') if wait > 10
407
+
408
+ Entry.new('IO Wait', wait)
409
+ end
410
+
411
+ # Internal: Determine the amount of disk space in use on the target host.
412
+ #
413
+ # Returns an Entry struct.
414
+ def determine_storage_disk_usage
415
+ mounts = query('df').lines.select do |line|
416
+ fs, blocks, _ = line.split(/\s+/, 3)
417
+ /^\/dev/.match(fs) || blocks.to_i > 10000000
418
+ end
419
+
420
+ usage = mounts.reduce(0) do |collector, mount|
421
+ collector + mount.split(/\s+/, 4)[2].to_i
422
+ end
423
+
424
+ usage = sprintf('%.1f', usage.to_f / 1000**2)
425
+
426
+ warn('Unable to find meaningful mounts') if mounts.empty?
427
+ warn('Unable to determine disk usage') if usage.to_f < 1
428
+
429
+ Entry.new('Disk Usage', "#{usage} GB")
430
+ end
431
+
432
+ # Internal: Determine public IPv4 addresses in use by the target host.
433
+ #
434
+ # Returns an Entry struct.
435
+ def determine_network_public_v4_ips
436
+ addresses = list_v4_ips
437
+ addresses.reject! { |ip| rfc1918?(ip) }
438
+
439
+ Entry.new('Public IPs', addresses.sort.join(', '))
440
+ end
441
+
442
+ # Internal: Determine private IPv4 addresses in use by the target host.
443
+ #
444
+ # Returns an Entry struct.
445
+ def determine_network_private_v4_ips
446
+ addresses = list_v4_ips
447
+ addresses.select! { |ip| rfc1918?(ip) }
448
+
449
+ Entry.new('Private IPs', addresses.sort.join(', '))
450
+ end
451
+
452
+ # Internal: Determine which perl version (if any) is installed on the
453
+ # target host.
454
+ #
455
+ # Returns an Entry struct.
456
+ def determine_library_perl
457
+ perl = query("perl -e 'print $^V;'")
458
+ perl.gsub!(/^v([0-9.]*).*/, '\1')
459
+ perl = '' unless /[0-9]/.match(perl)
460
+
461
+ Entry.new('Perl', perl)
462
+ end
463
+
464
+ # Internal: Determine which python version (if any) is installed on the
465
+ # target host.
466
+ #
467
+ # Returns an Entry struct.
468
+ def determine_library_python
469
+ python = query('python -c "import sys; print sys.version"')
470
+ python.gsub!(/([0-9.]*).*/m, '\1')
471
+ python = '' unless /\d/.match(python)
472
+
473
+ Entry.new('Python', python)
474
+ end
475
+
476
+ # Internal: Determine which ruby version (if any) is installed on the
477
+ # target host.
478
+ #
479
+ # Returns an Entry struct.
480
+ def determine_library_ruby
481
+ ruby = query('ruby -e "print RUBY_VERSION"')
482
+ ruby = '' unless /\d/.match(ruby)
483
+
484
+ Entry.new('Ruby', ruby)
485
+ end
486
+
487
+ # Internal: Determine which php version (if any) is installed on the target
488
+ # host.
489
+ #
490
+ # Returns an Entry struct.
491
+ def determine_library_php
492
+ php = query('php -v').lines.to_a[0].to_s
493
+ php.gsub!(/^PHP ([0-9.]*).*/, '\1')
494
+ php = '' unless /\d/.match(php)
495
+
496
+ Entry.new('PHP', php)
497
+ end
498
+
499
+ # Internal: Gather a list of all listening ports on the target host.
500
+ #
501
+ # Returns nothing.
502
+ def determine_services_ports
503
+ netstat = as_root('netstat -untlp')
504
+ netstat = netstat.lines.select { |line| /^[tu][cd]p/.match(line) }
505
+ netstat.map! { |line| line.split(/\s+/) }
506
+
507
+ addresses = netstat.map { |row| row[3].gsub(/:[^:]*$/, '') }.uniq.sort
508
+ netstat.map! do |row|
509
+ port = row[3].gsub(/.*:/, '')
510
+ pid = row[-1].gsub(/.*\//, '')
511
+ " " * 16 + "% 6d %s" % [port, pid]
512
+ end
513
+ netstat.uniq!
514
+ netstat.sort! { |x,y| x.to_i <=> y.to_i }
515
+
516
+ warn('Cannot enumerate listening ports') if netstat.empty?
517
+
518
+ netstat[0] = netstat[0].to_s[16..-1]
519
+ Entry.new('Listening Ports', "#{netstat.join("\n")}")
520
+ end
521
+
522
+ # Internal: Check for signs of running control panels.
523
+ #
524
+ # Returns nothing.
525
+ def warning_control_panels
526
+ if @process_list.grep(/psa/i).any?
527
+ warn('Server likely to be running Plesk')
528
+ end
529
+ if @process_list.grep(/cpanel/i).any?
530
+ warn('Server likely to be running cPanel')
531
+ end
532
+ end
533
+
534
+ # Internal: Enumerate v4 IPs on the host.
535
+ #
536
+ # Returns an Array of IPv4 addresses outside of 127/8
537
+ def list_v4_ips
538
+ addresses = query('/sbin/ifconfig').lines.select do |line|
539
+ /inet[^6]/.match(line)
540
+ end
541
+ addresses.map! { |ip| ip.split(/\s+/).grep(/(?:\d+\.){3}\d+/)[0] }
542
+ addresses.map! { |ip| ip.gsub(/[^\d]*((?:\d+\.){3}\d+)[^\d]*/, '\\1')}
543
+ addresses.reject! { |ip| /127(\.\d+){3}/.match(ip) }
544
+ end
545
+
546
+ # Internal: Wrap SSH#query
547
+ #
548
+ # args - Globbed args to pass to SSH object
549
+ #
550
+ # Returns a String
551
+ def query(*args)
552
+ @shell.query(*args)
553
+ end
554
+
555
+ # Internal: Wrap SSH#as_root
556
+ #
557
+ # args - Globbed args to pass to SSH object
558
+ #
559
+ # Returns a String
560
+ def as_root(*args)
561
+ @shell.as_root(*args)
562
+ end
563
+
564
+ # Internal: Add a warning to the list of warnings encountered.
565
+ #
566
+ # warning - String containing warning text.
567
+ #
568
+ # Returns nothing.
569
+ def warn(warning)
570
+ @warnings.push warning
571
+ end
572
+
573
+ # Internal: Determine and set CPE representative of the running system.
574
+ # Resort to a best guess if this cannot be reliably accomplished.
575
+ #
576
+ # Sets @cpe.
577
+ #
578
+ # Returns nothing.
579
+ def set_system_cpe
580
+ # Some distros ship with a file containing the CPE for their platform;
581
+ # this should be used if at all possible.
582
+ release = query('cat /etc/system-release-cpe')
583
+ begin
584
+ cpe = CPE.parse(release)
585
+ cpe.version.gsub!(/[^0-9.]/, '')
586
+ @cpe = cpe
587
+ return
588
+ rescue ArgumentError
589
+ end
590
+
591
+ cpe = CPE.new(part: CPE::OS)
592
+
593
+ # Depend on the reported kernel name for product name
594
+ cpe.product = query('uname -s')
595
+
596
+ # Depend on /etc/issue if it's available
597
+ issue = query('cat /etc/issue')
598
+ cpe.vendor = distro_name(issue)
599
+
600
+ # If /etc/issue fails, resort to looking any release/version file
601
+ if cpe.vendor.empty?
602
+ release = query("grep -h '^ID=' /etc/*[_-][rv]e[lr]*").lines.first
603
+ cpe.vendor = distro_name(release)
604
+ end
605
+
606
+ # Fall back to depending on the OS reported by uname if all else fails
607
+ cpe.vendor = query('uname -o') if cpe.vendor.empty?
608
+
609
+ # Version number will be determined from /etc/issue
610
+ cpe.version = version_number(issue)
611
+
612
+ # If Version is not represented, fall back to kernel reported version
613
+ cpe.version = version_number(query('uname -r')) if cpe.version.empty?
614
+ @cpe = cpe
615
+ end
616
+
617
+ # Internal: Search for nicely formatted names of Linux distributions in a
618
+ # string which may contain the name of the distribution currently running
619
+ # on the target host. If multiple matches exist, resort to the first one.
620
+ #
621
+ # Returns a String.
622
+ def distro_name(str)
623
+ matches = DISTRO_NAMES.select do |distro|
624
+ Regexp.new(distro, Regexp::IGNORECASE).match(str)
625
+ end
626
+ matches.first.to_s
627
+ end
628
+
629
+ # Internal: Inspect a String which may contain a version number. Sanitize
630
+ # the version number, removing any extraneous information.
631
+ #
632
+ # Returns a String.
633
+ def version_number(str)
634
+ if str =~ /\d/
635
+ str.gsub(/^[^\d]*/, '').gsub(/[^\d]*$/, '').gsub(/(\d*\.\d*).*/, '\1')
636
+ else
637
+ ''
638
+ end
639
+ end
640
+
641
+ # Internal: Determine if a v4 IP address belongs to a private (RFC 1918)
642
+ # network.
643
+ #
644
+ # ip - String containing an IP.
645
+ #
646
+ # Returns true if the IP falls within the private range, false otherwise.
647
+ def rfc1918?(ip)
648
+ Addrinfo.ip(ip).ipv4_private?
649
+ end
650
+ end
651
+ end; end
data/lib/cloudflock.rb CHANGED
@@ -1,10 +1,8 @@
1
+ require 'cloudflock/error'
2
+ require 'cloudflock/errstr'
3
+
4
+ # Public: Encapsulate all functionality related to the CloudFlock API and any
5
+ # apps built with such.
1
6
  module CloudFlock
2
- module Remote; end
3
- module Interface; end
4
- module Target;
5
- module Servers; end
6
- end
7
+ VERSION = '0.7.0'
7
8
  end
8
-
9
- require 'cloudflock/version'
10
- require 'cloudflock/error'