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
@@ -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'