rouster 0.57 → 0.61

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.
@@ -12,7 +12,7 @@ require 'rouster/vagrant'
12
12
  class Rouster
13
13
 
14
14
  # sporadically updated version number
15
- VERSION = 0.57
15
+ VERSION = 0.61
16
16
 
17
17
  # custom exceptions -- what else do we want them to include/do?
18
18
  class ArgumentError < StandardError; end # thrown by methods that take parameters from users
@@ -76,15 +76,6 @@ class Rouster
76
76
  @verbosity_logfile = 2 # this is kind of arbitrary, but won't actually be created unless opts[:logfile] is also passed
77
77
  end
78
78
 
79
- if opts.has_key?(:sudo)
80
- @sudo = opts[:sudo]
81
- elsif @passthrough.class.eql?(Hash)
82
- # TODO say something here.. or maybe check to see if our user has passwordless sudo?
83
- @sudo = false
84
- else
85
- @sudo = true
86
- end
87
-
88
79
  @ostype = nil
89
80
  @output = Array.new
90
81
  @cache = Hash.new
@@ -109,26 +100,98 @@ class Rouster
109
100
 
110
101
  @logger.outputters[0].level = @verbosity_console # can't set this when instantiating a .std* logger, and want the FileOutputter at a different level
111
102
 
103
+ if opts.has_key?(:sudo)
104
+ @sudo = opts[:sudo]
105
+ elsif @passthrough.class.eql?(Hash)
106
+ @logger.debug(sprintf('passthrough without sudo specification, defaulting to false'))
107
+ @sudo = false
108
+ else
109
+ @sudo = true
110
+ end
111
+
112
112
  if @passthrough
113
- # TODO do better about informing of required specifications, maybe point them to an URL?
114
113
  @vagrantbinary = 'vagrant' # hacky fix to is_vagrant_running?() grepping, doesn't need to actually be in $PATH
114
+ @sshtunnel = opts[:sshtunnel].nil? ? false : @sshtunnel # unless user has specified it, non-local passthroughs default to not open tunnel
115
+
116
+ defaults = {
117
+ :paranoid => false, # valid overrides are: false, true, :very, or :secure
118
+ :ssh_sleep_ceiling => 9,
119
+ :ssh_sleep_time => 10,
120
+ }
121
+
122
+ @passthrough = defaults.merge(@passthrough)
123
+
115
124
  if @passthrough.class != Hash
116
125
  raise ArgumentError.new('passthrough specification should be hash')
117
126
  elsif @passthrough[:type].nil?
118
- raise ArgumentError.new('passthrough :type must be specified, :local or :remote allowed')
127
+ raise ArgumentError.new('passthrough :type must be specified, :local, :remote or :aws allowed')
119
128
  elsif @passthrough[:type].eql?(:local)
120
- @sshtunnel = false
121
129
  @logger.debug('instantiating a local passthrough worker')
130
+ @sshtunnel = opts[:sshtunnel].nil? ? true : opts[:sshtunnel] # override default, if local, open immediately
131
+
122
132
  elsif @passthrough[:type].eql?(:remote)
123
- raise ArgumentError.new('remote passthrough requires :host specification') if @passthrough[:host].nil?
124
- raise ArgumentError.new('remote passthrough requires :user specification') if @passthrough[:user].nil?
125
- raise ArgumentError.new('remote passthrough requires :key specification') if @passthrough[:key].nil?
133
+ @logger.debug('instantiating a remote passthrough worker')
134
+
135
+ [:host, :user, :key].each do |r|
136
+ raise ArgumentError.new(sprintf('remote passthrough requires[%s] specification', r)) if @passthrough[r].nil?
137
+ end
138
+
126
139
  raise ArgumentError.new('remote passthrough requires valid :key specification, should be path to private half') unless File.file?(@passthrough[:key])
127
140
  @sshkey = @passthrough[:key] # TODO refactor so that you don't have to do this..
128
- @logger.debug('instantiating a remote passthrough worker')
141
+
142
+ elsif @passthrough[:type].eql?(:aws) or @passthrough[:type].eql?(:raiden)
143
+ @logger.debug(sprintf('instantiating an %s passthrough worker', @passthrough[:type]))
144
+
145
+ aws_defaults = {
146
+ :ami => 'ami-7bdaa84b', # RHEL 6.5 x64 in us-west-2
147
+ :dns_propagation_sleep => 30, # how much time to wait after ELB creation before attempting to connect
148
+ :key_id => ENV['AWS_ACCESS_KEY_ID'],
149
+ :min_count => 1,
150
+ :max_count => 1,
151
+ :region => 'us-west-2',
152
+ :secret_key => ENV['AWS_SECRET_ACCESS_KEY'],
153
+ :size => 't1.micro',
154
+ :ssh_port => 22,
155
+ :user => 'ec2-user',
156
+ }
157
+
158
+ if @passthrough.has_key?(:ami)
159
+ @logger.debug(':ami specified, will start new EC2 instance')
160
+
161
+ @passthrough[:security_groups] = @passthrough[:security_groups].is_a?(Array) ? @passthrough[:security_groups] : [ @passthrough[:security_groups] ]
162
+
163
+ @passthrough = aws_defaults.merge(@passthrough)
164
+
165
+ [:ami, :size, :user, :region, :key, :keypair, :key_id, :secret_key, :security_groups].each do |r|
166
+ raise ArgumentError.new(sprintf('AWS passthrough requires %s specification', r)) if @passthrough[r].nil?
167
+ end
168
+
169
+ elsif @passthrough.has_key?(:instance)
170
+ @logger.debug(':instance specified, will connect to existing EC2 instance')
171
+
172
+ @passthrough = aws_defaults.merge(@passthrough)
173
+
174
+ if @passthrough[:type].eql?(:aws)
175
+ @passthrough[:host] = self.aws_describe_instance(@passthrough[:instance])['dnsName']
176
+ else
177
+ @passthrough[:host] = self.find_ssh_elb(true)
178
+ end
179
+
180
+ [:instance, :key, :user, :host].each do |r|
181
+ raise ArgumentError.new(sprintf('AWS passthrough requires [%s] specification', r)) if @passthrough[r].nil?
182
+ end
183
+
184
+ else
185
+ raise ArgumentError.new('AWS passthrough requires either :ami or :instance specification')
186
+ end
187
+
188
+ raise ArgumentError.new('AWS passthrough requires valid :sshkey specification, should be path to private half') unless File.file?(@passthrough[:key])
189
+ @sshkey = @passthrough[:key]
190
+
129
191
  else
130
- raise ArgumentError.new(sprintf('passthrough :type [%s] unknown, allowed: :local, :remote', @passthrough[:type]))
192
+ raise ArgumentError.new(sprintf('passthrough :type [%s] unknown, allowed: :aws, :local, :remote', @passthrough[:type]))
131
193
  end
194
+
132
195
  else
133
196
 
134
197
  @logger.debug('Vagrantfile and VM name validation..')
@@ -154,14 +217,6 @@ class Rouster
154
217
  end
155
218
  end
156
219
 
157
- # this is breaking test/functional/test_caching.rb test_ssh_caching (if the VM was not running when the test started)
158
- # it slows down object instantiation, but is a good test to ensure the machine name is valid..
159
- begin
160
- self.status()
161
- rescue Rouster::LocalExecutionError => e
162
- raise InternalError.new(sprintf('caught non-0 exitcode from status(): %s', e.message))
163
- end
164
-
165
220
  begin
166
221
  raise InternalError.new('ssh key not specified') if @sshkey.nil?
167
222
  raise InternalError.new('ssh key does not exist') unless File.file?(@sshkey)
@@ -282,23 +337,22 @@ class Rouster
282
337
 
283
338
  if @ssh.nil? or @ssh.closed?
284
339
  begin
285
- self.connect_ssh_tunnel()
340
+ res = self.connect_ssh_tunnel()
286
341
  rescue Rouster::InternalError, Net::SSH::Disconnect => e
287
342
  res = false
288
343
  end
289
344
 
290
345
  end
291
346
 
292
- if res.nil?
347
+ if res.nil? or res.is_a?(Net::SSH::Connection::Session)
293
348
  begin
294
349
  self.run('echo functional test of SSH tunnel')
350
+ res = true
295
351
  rescue
296
352
  res = false
297
353
  end
298
354
  end
299
355
 
300
- res = true if res.nil?
301
-
302
356
  if @cache_timeout
303
357
  @cache[:is_available_via_ssh?] = Hash.new unless @cache[:is_available_via_ssh?].class.eql?(Hash)
304
358
  @cache[:is_available_via_ssh?][:time] = Time.now.to_i
@@ -360,18 +414,36 @@ class Rouster
360
414
  def connect_ssh_tunnel
361
415
 
362
416
  if self.is_passthrough?
363
- if self.passthrough[:type].eql?(:local)
417
+ if @passthrough[:type].eql?(:local)
418
+ @logger.debug("local passthroughs don't need ssh tunnel, shell execs are used")
419
+ return false
420
+ elsif @passthrough[:host].nil?
421
+ @logger.info(sprintf('not attempting to connect, no known hostname for[%s]', self.passthrough))
364
422
  return false
365
423
  else
366
- @logger.debug('opening remote SSH tunnel..')
367
- @ssh = Net::SSH.start(
368
- @passthrough[:host],
369
- @passthrough[:user],
370
- :port => @passthrough[:port],
371
- :keys => [ @passthrough[:key] ],
372
- :paranoid => false
373
- )
424
+ ceiling = @passthrough[:ssh_sleep_ceiling]
425
+ sleep_time = @passthrough[:ssh_sleep_time]
426
+
427
+ 0.upto(ceiling) do |try|
428
+ @logger.debug(sprintf('opening remote SSH tunnel[%s]..', @passthrough[:host]))
429
+ begin
430
+ @ssh = Net::SSH.start(
431
+ @passthrough[:host],
432
+ @passthrough[:user],
433
+ :port => @passthrough[:ssh_port],
434
+ :keys => [ @passthrough[:key] ], # TODO this should be @sshkey
435
+ :paranoid => false
436
+ )
437
+ break
438
+ rescue => e
439
+ raise e if try.eql?(ceiling) # eventually want to throw a SocketError
440
+ @logger.debug(sprintf('failed to open tunnel[%s], trying again in %ss', e.message, sleep_time))
441
+ sleep sleep_time
442
+ end
443
+ end
374
444
  end
445
+ @logger.debug(sprintf('successfully opened SSH tunnel to[%s]', passthrough[:host]))
446
+
375
447
  else
376
448
  # not a passthrough, normal connection
377
449
  status = self.status()
@@ -387,6 +459,7 @@ class Rouster
387
459
  :paranoid => false
388
460
  )
389
461
  else
462
+ # TODO will we ever hit this? or will we be thrown first?
390
463
  raise InternalError.new(sprintf('VM is not running[%s], unable open SSH tunnel', status))
391
464
  end
392
465
  end
@@ -416,32 +489,25 @@ class Rouster
416
489
  return @ostype
417
490
  end
418
491
 
419
- # TODO switch to file based detection
420
- # Ubuntu - /etc/os-release
421
- # Solaris - /etc/release
422
- # RHEL/CentOS - /etc/redhat-release
423
- # OSX - ?
492
+ files = {
493
+ :ubuntu => '/etc/os-release', # debian too
494
+ :solaris => '/etc/release',
495
+ :redhat => '/etc/redhat-release', # centos too
496
+ :osx => '/System/Library/CoreServices/SystemVersion.plist',
497
+ }
424
498
 
425
499
  res = nil
426
- uname = self.run('uname -a')
427
-
428
- case uname
429
- when /Darwin/i
430
- res = :osx
431
- when /SunOS|Solaris/i
432
- res =:solaris
433
- when /Ubuntu/i
434
- res = :ubuntu
435
- when /Debian/i
436
- res = :debian
437
- else
438
- if self.is_file?('/etc/redhat-release')
439
- res = :redhat
440
- else
441
- res = nil
442
- end
500
+
501
+ files.each do |os,file|
502
+ if self.is_file?(file)
503
+ @logger.debug(sprintf('determined OS to be[%s] via[%s]', os, file))
504
+ res = os
505
+ break
506
+ end
443
507
  end
444
508
 
509
+ @logger.error(sprintf('unable to determine OS, looking for[%s]', files)) if res.nil?
510
+
445
511
  @ostype = res
446
512
  res
447
513
  end
@@ -493,6 +559,7 @@ class Rouster
493
559
  raise FileTransferError.new(sprintf('unable to put[%s], exception[%s]', local_file, e.message()))
494
560
  end
495
561
 
562
+ return true
496
563
  end
497
564
 
498
565
  ##
@@ -213,9 +213,9 @@ class Rouster
213
213
  #
214
214
  # supported OS
215
215
  # * OSX - runs `pkgutil --pkgs` and `pkgutil --pkg-info=<package>` (if deep)
216
- # * RedHat - runs `rpm -qa`
216
+ # * RedHat - runs `rpm -qa --qf "%{n}@%{v}@%{arch}\n"` (does not support deep)
217
217
  # * Solaris - runs `pkginfo` and `pkginfo -l <package>` (if deep)
218
- # * Ubuntu - runs `dpkg --get-selections` and `dpkg -s <package>` (if deep)
218
+ # * Ubuntu - runs `dpkg-query -W -f='${Package}\@${Version}\@${Architecture}\n'` (does not support deep)
219
219
  #
220
220
  # raises InternalError if unsupported operating system
221
221
  def get_packages(cache=true, deep=true)
@@ -239,62 +239,93 @@ class Rouster
239
239
 
240
240
  raw = self.run('pkgutil --pkgs')
241
241
  raw.split("\n").each do |line|
242
+ name = line
243
+ arch = '?'
242
244
  version = '?'
243
245
 
244
246
  if deep
245
247
  # can get install time, volume and location as well
246
- local_res = self.run(sprintf('pkgutil --pkg-info=%s', line))
247
- version = $1 if local_res.match(/version\:\s+(.*?)$/)
248
+ local_res = self.run(sprintf('pkgutil --pkg-info=%s', name))
249
+ version = $1 if local_res.match(/version\:\s+(.*?)$/)
250
+ end
251
+
252
+ if res.has_key?(name)
253
+ # different architecture of an already known package
254
+ @logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
255
+ new_element = { :version => version, :arch => arch }
256
+ res[name] = [ res[name], new_element ]
257
+ else
258
+ res[name] = { :version => version, :arch => arch }
248
259
  end
249
260
 
250
- res[line] = version
251
261
  end
252
262
 
253
263
  elsif os.eql?(:solaris)
254
264
  raw = self.run('pkginfo')
255
265
  raw.split("\n").each do |line|
256
- next if line.match(/(.*?)\s+(.*?)\s(.*)$/).empty?
266
+ next if line.match(/(.*?)\s+(.*?)\s(.*)$/).nil?
257
267
  name = $2
268
+ arch = '?'
258
269
  version = '?'
259
270
 
260
271
  if deep
261
- local_res = self.run(sprintf('pkginfo -l %s', name))
262
- version = $1 if local_res.match(/VERSION\:\s+(.*?)$/i)
272
+ begin
273
+ # who throws non-0 exit codes when querying for legit package information? solaris does.
274
+ local_res = self.run(sprintf('pkginfo -l %s', name))
275
+ arch = $1 if local_res.match(/ARCH\:\s+(.*?)$/)
276
+ version = $1 if local_res.match(/VERSION\:\s+(.*?)$/)
277
+ rescue
278
+ arch = '?' if arch.nil?
279
+ version = '?' if arch.nil?
280
+ end
263
281
  end
264
282
 
265
- res[name] = version
283
+ if res.has_key?(name)
284
+ # different architecture of an already known package
285
+ @logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
286
+ new_element = { :version => version, :arch => arch }
287
+ res[name] = [ res[name], new_element ]
288
+ else
289
+ res[name] = { :version => version, :arch => arch }
290
+ end
266
291
  end
267
292
 
268
293
  elsif os.eql?(:ubuntu) or os.eql?(:debian)
269
- raw = self.run('dpkg --get-selections')
294
+ raw = self.run("dpkg-query -W -f='${Package}@${Version}@${Architecture}\n'")
270
295
  raw.split("\n").each do |line|
271
- next if line.match(/^(.*?)\s/).nil?
296
+ next if line.match(/(.*?)\@(.*?)\@(.*)/).nil?
272
297
  name = $1
273
- version = '?'
274
-
275
- if deep
276
- local_res = self.run(sprintf('dpkg -s %s', name))
277
- version = $1 if local_res.match(/Version\:\s(.*?)$/)
298
+ version = $2
299
+ arch = $3
300
+
301
+ if res.has_key?(name)
302
+ # different architecture of an already known package
303
+ @logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
304
+ new_element = { :version => version, :arch => arch }
305
+ res[name] = [ res[name], new_element ]
306
+ else
307
+ res[name] = { :version => version, :arch => arch }
278
308
  end
279
309
 
280
- res[name] = version
281
310
  end
282
311
 
283
312
  elsif os.eql?(:redhat)
284
- raw = self.run('rpm -qa')
313
+ raw = self.run('rpm -qa --qf "%{n}@%{v}@%{arch}\n"')
285
314
  raw.split("\n").each do |line|
286
- next if line.match(/(.*?)-(\d*\..*)/).nil? # ht petersen.allen
287
- #next if line.match(/(.*)-(\d+\.\d+.*)/).nil? # another alternate, but still not perfect
315
+ next if line.match(/(.*?)\@(.*?)\@(.*)/).nil?
288
316
  name = $1
289
- version = '?' # we could use $2, but we don't trust it
290
-
291
- if deep
292
- local_res = self.run(sprintf('rpm -qi %s', line))
293
- name = $1 if local_res.match(/Name\s+:\s(\S*)/)
294
- version = $1 if local_res.match(/Version\s+:\s(\S*)/)
317
+ version = $2
318
+ arch = $3
319
+
320
+ if res.has_key?(name)
321
+ # different architecture of an already known package
322
+ @logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
323
+ new_element = { :version => version, :arch => arch }
324
+ res[name] = [ res[name], new_element ]
325
+ else
326
+ res[name] = { :version => version, :arch => arch }
295
327
  end
296
328
 
297
- res[name] = version
298
329
  end
299
330
 
300
331
  else
@@ -389,17 +420,20 @@ class Rouster
389
420
  # parameters
390
421
  # * [cache] - boolean controlling whether data retrieved/parsed is cached, defaults to true
391
422
  # * [humanize] - boolean controlling whether data retrieved is massaged into simplified names or returned mostly as retrieved
423
+ # * [type] - symbol indicating which service controller to query, defaults to :all
424
+ # * [seed] - test hook to seed the output of service commands
392
425
  #
393
- # supported OS
394
- # * OSX - runs `launchctl list`
395
- # * RedHat - runs `/sbin/service --status-all`
396
- # * Solaris - runs `svcs`
397
- # * Ubuntu - runs `service --status-all`
426
+ # supported OS and types
427
+ # * OSX - :launchd
428
+ # * RedHat - :systemv or :upstart
429
+ # * Solaris - :smf
430
+ # * Ubuntu - :systemv or :upstart
398
431
  #
399
432
  # notes
400
433
  # * raises InternalError if unsupported operating system
401
434
  # * OSX, Solaris and Ubuntu/Debian will only return running|stopped|unsure, the exists|installed|operational modes are RHEL/CentOS only
402
- def get_services(cache=true, humanize=true)
435
+
436
+ def get_services(cache=true, humanize=true, type=:all, seed=nil)
403
437
  if cache and ! self.deltas[:services].nil?
404
438
 
405
439
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:services]) > self.cache_timeout
@@ -415,132 +449,234 @@ class Rouster
415
449
  res = Hash.new()
416
450
  os = self.os_type
417
451
 
418
- allowed_modes = %w(exists installed operational running stopped unsure)
419
- failover_mode = 'unsure'
452
+ commands = {
453
+ :osx => {
454
+ :launchd => 'launchctl list',
455
+ },
456
+ :solaris => {
457
+ :smf => 'svcs -a',
458
+ },
459
+
460
+ # TODO we really need to implement something like osfamily
461
+ :ubuntu => {
462
+ :systemv => 'service --status-all 2>&1',
463
+ :upstart => 'initctl list',
464
+ },
465
+ :debian => {
466
+ :systemv => 'service --status-all 2>&1',
467
+ :upstart => 'initctl list',
468
+ },
469
+ :redhat => {
470
+ :systemv => '/sbin/service --status-all',
471
+ :upstart => 'initctl list',
472
+ },
473
+ }
474
+
475
+ if type.eql?(:all)
476
+ type = commands[os].keys
477
+ end
420
478
 
421
- if os.eql?(:osx)
479
+ type = type.class.eql?(Array) ? type : [ type ]
422
480
 
423
- raw = self.run('launchctl list')
424
- raw.split("\n").each do |line|
425
- next if line.match(/(?:\S*?)\s+(\S*?)\s+(\S*)$/).nil?
481
+ type.each do |provider|
426
482
 
427
- service = $2
428
- mode = $1
483
+ raise InternalError.new(sprintf('unable to get service information from VM operating system[%s]', os)) unless commands.has_key?(os)
484
+ raise ArgumentError.new(sprintf('unable to find command provider[%s] for [%s]', provider, os)) if commands[os][provider].nil?
429
485
 
430
- if humanize # should we do this with a .freeze instead?
431
- if mode.match(/^\d/)
432
- mode = 'running'
433
- else
434
- mode = 'stopped'
435
- end
436
- end
486
+ @logger.info(sprintf('get_services using provider [%s] on [%s]', provider, os))
437
487
 
438
- res[service] = mode
439
- end
488
+ # TODO while this is true, what if self.user is 'root'.. -- the problem is we don't have self.user, and we store this data differently depending on self.passthrough?
489
+ @logger.warn('gathering service information typically works better with sudo, which is currently not being used') unless self.uses_sudo?
440
490
 
441
- elsif os.eql?(:solaris)
491
+ # TODO come up with a better test hook -- real problem is that we can't seed 'raw' with different values per iteration
492
+ raw = seed.nil? ? self.run(commands[os][provider]) : seed
442
493
 
443
- raw = self.run('svcs -a')
444
- raw.split("\n").each do |line|
445
- next if line.match(/(.*?)\s+(?:.*?)\s+(.*?)$/).nil?
446
-
447
- service = $2
448
- mode = $1
449
-
450
- if humanize
451
- if mode.match(/^online/)
452
- mode = 'running'
453
- elsif mode.match(/^legacy_run/)
454
- mode = 'running'
455
- elsif mode.match(/^disabled/)
456
- mode = 'stopped'
457
- end
494
+ if os.eql?(:osx)
495
+
496
+ raw.split("\n").each do |line|
497
+ next if line.match(/(?:\S*?)\s+(\S*?)\s+(\S+)$/).nil?
498
+ tokens = line.split("\s")
499
+ service = tokens[-1]
500
+ mode = tokens[0]
458
501
 
459
- if service.match(/^svc:\/.*\/(.*?):.*/)
460
- # turning 'svc:/network/cswpuppetd:default' into 'cswpuppetd'
461
- service = $1
462
- elsif service.match(/^lrc:\/.*?\/.*\/(.*)/)
463
- # turning 'lrc:/etc/rcS_d/S50sk98Sol' into 'S50sk98Sol'
464
- service = $1
502
+ if humanize # should we do this with a .freeze instead?
503
+ if mode.match(/^\d/)
504
+ mode = 'running'
505
+ elsif mode.match(/-/)
506
+ mode = 'stopped'
507
+ else
508
+ next # this should handle the banner "PID Status Label"
509
+ end
465
510
  end
511
+
512
+ res[service] = mode
466
513
  end
467
514
 
468
- res[service] = mode
515
+ elsif os.eql?(:solaris)
469
516
 
470
- end
517
+ raw.split("\n").each do |line|
518
+ next if line.match(/(.*?)\s+(?:.*?)\s+(.*?)$/).nil?
471
519
 
472
- elsif os.eql?(:ubuntu) or os.eql?(:debian)
520
+ service = $2
521
+ mode = $1
473
522
 
474
- raw = self.run('service --status-all 2>&1')
475
- raw.split("\n").each do |line|
476
- next if line.match(/\[(.*?)\]\s+(.*)$/).nil?
477
- mode = $1
478
- service = $2
479
-
480
- if humanize
481
- mode = 'stopped' if mode.match('-')
482
- mode = 'running' if mode.match('\+')
483
- mode = 'unsure' if mode.match('\?')
484
- end
523
+ if humanize
524
+ if mode.match(/^online/)
525
+ mode = 'running'
526
+ elsif mode.match(/^legacy_run/)
527
+ mode = 'running'
528
+ elsif mode.match(/^disabled/)
529
+ mode = 'stopped'
530
+ end
485
531
 
486
- res[service] = mode
487
- end
532
+ if service.match(/^svc:\/.*\/(.*?):.*/)
533
+ # turning 'svc:/network/cswpuppetd:default' into 'cswpuppetd'
534
+ service = $1
535
+ elsif service.match(/^lrc:\/.*?\/.*\/(.*)/)
536
+ # turning 'lrc:/etc/rcS_d/S50sk98Sol' into 'S50sk98Sol'
537
+ service = $1
538
+ end
539
+ end
488
540
 
489
- elsif os.eql?(:redhat)
541
+ res[service] = mode
490
542
 
491
- raw = self.run('/sbin/service --status-all')
492
- raw.split("\n").each do |line|
543
+ end
493
544
 
494
- if humanize
545
+ elsif os.eql?(:ubuntu) or os.eql?(:debian)
546
+
547
+ raw.split("\n").each do |line|
548
+ if provider.eql?(:systemv)
549
+ next if line.match(/\[(.*?)\]\s+(.*)$/).nil?
550
+ mode = $1
551
+ service = $2
552
+
553
+ if humanize
554
+ mode = 'stopped' if mode.match('-')
555
+ mode = 'running' if mode.match('\+')
556
+ mode = 'unsure' if mode.match('\?')
557
+ end
558
+
559
+ res[service] = mode
560
+ elsif provider.eql?(:upstart)
561
+ if line.match(/(.*?)\s.*?(.*?),/)
562
+ # tty (/dev/tty3) start/running, process 1601
563
+ # named start/running, process 8959
564
+ service = $1
565
+ mode = $2
566
+ elsif line.match(/(.*?)\s(.*)/)
567
+ # rcS stop/waiting
568
+ service = $1
569
+ mode = $2
570
+ else
571
+ @logger.warn("unable to process upstart line[#{line}], skipping")
572
+ next
573
+ end
574
+
575
+ if humanize
576
+ mode = 'stopped' if mode.match('stop/waiting')
577
+ mode = 'running' if mode.match('start/running')
578
+ mode = 'unsure' unless mode.eql?('stopped') or mode.eql?('running')
579
+ end
580
+
581
+ res[service] = mode
582
+ end
583
+ end
495
584
 
496
- if line.match(/^(\w+?)\sis\s(.*)$/)
497
- # <service> is <state>
498
- name = $1
499
- state = $2
500
- res[name] = state
585
+ elsif os.eql?(:redhat)
586
+
587
+ raw.split("\n").each do |line|
588
+ if provider.eql?(:systemv)
589
+ if humanize
590
+ if line.match(/^(\w+?)\sis\s(.*)$/)
591
+ # <service> is <state>
592
+ name = $1
593
+ state = $2
594
+ res[name] = state
595
+
596
+ if state.match(/^not/)
597
+ # this catches 'Kdump is not operational'
598
+ res[name] = 'stopped'
599
+ end
600
+
601
+ elsif line.match(/^(\w+?)\s\(pid.*?\)\sis\s(\w+)$/)
602
+ # <service> (pid <pid> [pid]) is <state>...
603
+ res[$1] = $2
604
+ elsif line.match(/^(\w+?)\sis\s(\w+)\.*$/) # not sure this is actually needed
605
+ @logger.debug('triggered supposedly unnecessary regex')
606
+ # <service> is <state>. whatever
607
+ res[$1] = $2
608
+ elsif line.match(/razor_daemon:\s(\w+).*$/)
609
+ # razor_daemon: running [pid 11325]
610
+ # razor_daemon: no instances running
611
+ res['razor_daemon'] = $1.eql?('running') ? $1 : 'stopped'
612
+ elsif line.match(/^(\w+?)\:.*?(\w+)$/)
613
+ # <service>: whatever <state>
614
+ res[$1] = $2
615
+ elsif line.match(/^(\w+?):.*?\sis\snot\srunning\.$/)
616
+ # ip6tables: Firewall is not running.
617
+ res[$1] = 'stopped'
618
+ elsif line.match(/^(\w+?)\s.*?\s(.*)$/)
619
+ # netconsole module not loaded
620
+ state = $2
621
+ res[$1] = $2.match(/not/) ? 'stopped' : 'running'
622
+ elsif line.match(/^(\w+)\s(\w+).*$/)
623
+ # <process> <state> whatever
624
+ res[$1] = $2
625
+ else
626
+ # original regex implementation, if we didn't match anything else, failover to this
627
+ next if line.match(/^([^\s:]*).*\s(\w*)(?:\.?){3}$/).nil?
628
+ res[$1] = $2
629
+ end
630
+ else
631
+ next if line.match(/^([^\s:]*).*\s(\w*)(?:\.?){3}$/).nil?
632
+ res[$1] = $2
633
+ end
634
+ elsif provider.eql?(:upstart)
635
+
636
+ if line.match(/(.*?)\s.*?(.*?),/)
637
+ # tty (/dev/tty3) start/running, process 1601
638
+ # named start/running, process 8959
639
+ service = $1
640
+ mode = $2
641
+ elsif line.match(/(.*?)\s(.*)/)
642
+ # rcS stop/waiting
643
+ service = $1
644
+ mode = $2
645
+ else
646
+ @logger.warn("unable to process upstart line[#{line}], skipping")
647
+ next
648
+ end
501
649
 
502
- if state.match(/^not/)
503
- # this catches 'Kdump is not operational'
504
- res[name] = 'stopped'
650
+ if humanize
651
+ mode = 'stopped' if mode.match('stop/waiting')
652
+ mode = 'running' if mode.match('start/running')
653
+ mode = 'unsure' unless mode.eql?('stopped') or mode.eql?('running')
505
654
  end
506
655
 
507
- elsif line.match(/^(\w+?)\s\(pid.*?\)\sis\s(\w+)$/)
508
- # <service> (pid <pid> [pid]) is <state>...
509
- res[$1] = $2
510
- elsif line.match(/^(\w+?)\sis\s(\w+)\.*$/) # not sure this is actually needed
511
- @logger.debug('triggered supposedly unnecessary regex')
512
- # <service> is <state>. whatever
513
- res[$1] = $2
514
- elsif line.match(/^(\w+?)\:.*?(\w+)$/)
515
- # <service>: whatever <state>
516
- res[$1] = $2
517
- elsif line.match(/^(\w+)\s(\w+).*$/)
518
- # <process> <state> whatever
519
- res[$1] = $2
520
- else
521
- # original regex implementation, if we didn't match anything else, failover to this
522
- next if line.match(/^([^\s:]*).*\s(\w*)(?:\.?){3}$/).nil?
523
- res[$1] = $2
656
+ res[service] = mode unless res.has_key?(service)
657
+
658
+
524
659
  end
525
660
 
526
- else
527
- next if line.match(/^([^\s:]*).*\s(\w*)(?:\.?){3}$/).nil?
528
- res[$1] = $2
529
661
  end
530
662
 
663
+ # end of os casing
531
664
  end
532
665
 
533
- # issue #63 handling
534
- if humanize
535
- res.each_pair do |k,v|
536
- next if allowed_modes.member?(v)
537
- @logger.debug(sprintf('replacing service[%s] status of [%s] with [%s] for uniformity', k, v, failover_mode))
538
- res[k] = failover_mode
539
- end
540
- end
666
+ # end of provider processing
667
+ end
541
668
 
542
- else
543
- raise InternalError.new(sprintf('unable to get service information from VM operating system[%s]', os))
669
+ # issue #63 handling
670
+ # TODO should we consider using symbols here instead?
671
+ allowed_modes = %w(exists installed operational running stopped unsure)
672
+ failover_mode = 'unsure'
673
+
674
+ if humanize
675
+ res.each_pair do |k,v|
676
+ next if allowed_modes.member?(v)
677
+ @logger.debug(sprintf('replacing service[%s] status of [%s] with [%s] for uniformity', k, v, failover_mode))
678
+ res[k] = failover_mode
679
+ end
544
680
  end
545
681
 
546
682
  if cache