rouster 0.57 → 0.61
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +67 -0
- data/README.md +179 -2
- data/Rakefile +8 -7
- data/Vagrantfile +24 -9
- data/examples/aws.rb +85 -0
- data/lib/rouster.rb +128 -61
- data/lib/rouster/deltas.rb +266 -130
- data/lib/rouster/puppet.rb +2 -2
- data/lib/rouster/testing.rb +28 -6
- data/lib/rouster/vagrant.rb +52 -16
- data/path_helper.rb +3 -4
- data/plugins/aws.rb +347 -0
- data/test/functional/deltas/test_get_packages.rb +50 -6
- data/test/functional/test_caching.rb +2 -2
- data/test/functional/test_new.rb +45 -4
- data/test/functional/test_passthroughs.rb +2 -2
- data/test/puppet/test_apply.rb +1 -1
- data/test/unit/test_new.rb +65 -0
- data/test/unit/testing/resources/osx-launchd +285 -0
- data/test/unit/testing/resources/rhel-systemv +40 -0
- data/test/unit/testing/resources/rhel-upstart +20 -0
- data/test/unit/testing/test_get_services.rb +151 -0
- data/test/unit/testing/test_validate_package.rb +36 -10
- metadata +11 -4
- data/test/puppet/test_roles.rb +0 -186
data/lib/rouster.rb
CHANGED
@@ -12,7 +12,7 @@ require 'rouster/vagrant'
|
|
12
12
|
class Rouster
|
13
13
|
|
14
14
|
# sporadically updated version number
|
15
|
-
VERSION = 0.
|
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 :
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
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
|
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
|
-
@
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
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
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
res =
|
431
|
-
|
432
|
-
|
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
|
##
|
data/lib/rouster/deltas.rb
CHANGED
@@ -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
|
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',
|
247
|
-
version
|
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(.*)$/).
|
266
|
+
next if line.match(/(.*?)\s+(.*?)\s(.*)$/).nil?
|
257
267
|
name = $2
|
268
|
+
arch = '?'
|
258
269
|
version = '?'
|
259
270
|
|
260
271
|
if deep
|
261
|
-
|
262
|
-
|
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
|
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(
|
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(
|
296
|
+
next if line.match(/(.*?)\@(.*?)\@(.*)/).nil?
|
272
297
|
name = $1
|
273
|
-
version =
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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(/(.*?)
|
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 =
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
395
|
-
# * RedHat
|
396
|
-
# * Solaris -
|
397
|
-
# * Ubuntu
|
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
|
-
|
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
|
-
|
419
|
-
|
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
|
-
|
479
|
+
type = type.class.eql?(Array) ? type : [ type ]
|
422
480
|
|
423
|
-
|
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
|
-
|
428
|
-
|
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
|
-
|
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
|
-
|
439
|
-
|
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
|
-
|
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
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
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
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
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
|
-
|
515
|
+
elsif os.eql?(:solaris)
|
469
516
|
|
470
|
-
|
517
|
+
raw.split("\n").each do |line|
|
518
|
+
next if line.match(/(.*?)\s+(?:.*?)\s+(.*?)$/).nil?
|
471
519
|
|
472
|
-
|
520
|
+
service = $2
|
521
|
+
mode = $1
|
473
522
|
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
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
|
-
|
487
|
-
|
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
|
-
|
541
|
+
res[service] = mode
|
490
542
|
|
491
|
-
|
492
|
-
raw.split("\n").each do |line|
|
543
|
+
end
|
493
544
|
|
494
|
-
|
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
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
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
|
503
|
-
|
504
|
-
|
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
|
-
|
508
|
-
|
509
|
-
|
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
|
-
#
|
534
|
-
|
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
|
-
|
543
|
-
|
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
|