rouster 0.7 → 0.41
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -3
- data/README.md +7 -241
- data/Rakefile +18 -55
- data/Vagrantfile +8 -26
- data/lib/rouster.rb +183 -404
- data/lib/rouster/deltas.rb +118 -577
- data/lib/rouster/puppet.rb +34 -209
- data/lib/rouster/testing.rb +59 -366
- data/lib/rouster/tests.rb +19 -70
- data/path_helper.rb +7 -5
- data/rouster.gemspec +1 -3
- data/test/basic.rb +1 -4
- data/test/functional/deltas/test_get_groups.rb +2 -74
- data/test/functional/deltas/test_get_packages.rb +4 -86
- data/test/functional/deltas/test_get_ports.rb +1 -26
- data/test/functional/deltas/test_get_services.rb +4 -43
- data/test/functional/deltas/test_get_users.rb +2 -35
- data/test/functional/puppet/test_facter.rb +1 -41
- data/test/functional/puppet/test_get_puppet_star.rb +68 -0
- data/test/functional/test_caching.rb +1 -5
- data/test/functional/test_dirs.rb +0 -25
- data/test/functional/test_get.rb +6 -10
- data/test/functional/test_inspect.rb +1 -1
- data/test/functional/test_is_file.rb +1 -17
- data/test/functional/test_new.rb +22 -233
- data/test/functional/test_put.rb +11 -9
- data/test/functional/test_restart.rb +4 -1
- data/test/functional/test_run.rb +3 -2
- data/test/puppet/test_apply.rb +11 -13
- data/test/puppet/test_roles.rb +173 -0
- data/test/unit/test_new.rb +0 -88
- data/test/unit/test_parse_ls_string.rb +0 -67
- data/test/unit/testing/test_validate_file.rb +47 -39
- data/test/unit/testing/test_validate_package.rb +10 -36
- metadata +6 -46
- data/.reek +0 -63
- data/.travis.yml +0 -11
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -102
- data/LICENSE +0 -9
- data/examples/aws.rb +0 -85
- data/examples/openstack.rb +0 -61
- data/examples/passthrough.rb +0 -71
- data/lib/rouster/vagrant.rb +0 -311
- data/plugins/aws.rb +0 -347
- data/plugins/openstack.rb +0 -136
- data/test/functional/deltas/test_get_crontab.rb +0 -161
- data/test/functional/deltas/test_get_os.rb +0 -68
- data/test/functional/test_is_in_file.rb +0 -40
- data/test/functional/test_passthroughs.rb +0 -94
- data/test/functional/test_validate_file.rb +0 -131
- data/test/unit/puppet/resources/puppet_run_with_failed_exec +0 -59
- data/test/unit/puppet/resources/puppet_run_with_successful_exec +0 -61
- data/test/unit/puppet/test_get_puppet_star.rb +0 -91
- data/test/unit/puppet/test_puppet_parsing.rb +0 -44
- data/test/unit/testing/resources/osx-launchd +0 -285
- data/test/unit/testing/resources/rhel-systemd +0 -46
- data/test/unit/testing/resources/rhel-systemv +0 -41
- data/test/unit/testing/resources/rhel-upstart +0 -20
- data/test/unit/testing/test_get_services.rb +0 -178
- data/test/unit/testing/test_validate_cron.rb +0 -78
- data/test/unit/testing/test_validate_port.rb +0 -103
data/lib/rouster/deltas.rb
CHANGED
@@ -1,119 +1,12 @@
|
|
1
1
|
require sprintf('%s/../../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
|
2
2
|
|
3
|
-
# deltas.rb - get information about
|
3
|
+
# deltas.rb - get information about groups, packages, services and users inside a Vagrant VM
|
4
4
|
require 'rouster'
|
5
5
|
require 'rouster/tests'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
##
|
10
|
-
# get_crontab
|
11
|
-
#
|
12
|
-
# runs `crontab -l <user>` and parses output, returns hash:
|
13
|
-
# {
|
14
|
-
# user => {
|
15
|
-
# command => {
|
16
|
-
# :minute => minute,
|
17
|
-
# :hour => hour,
|
18
|
-
# :dom => dom, # day of month
|
19
|
-
# :mon => mon, # month
|
20
|
-
# :dow => dow, # day of week
|
21
|
-
# }
|
22
|
-
# }
|
23
|
-
# }
|
24
|
-
#
|
25
|
-
# the hash will contain integers (not strings) for numerical values -- all but '*'
|
26
|
-
#
|
27
|
-
# parameters
|
28
|
-
# * <user> - name of user who owns crontab for examination -- or '*' to determine list of users and iterate over them to find all cron jobs
|
29
|
-
# * [cache] - boolean controlling whether or not retrieved/parsed data is cached, defaults to true
|
30
|
-
def get_crontab(user='root', cache=true)
|
31
|
-
|
32
|
-
if cache and self.deltas[:crontab].class.eql?(Hash)
|
33
|
-
|
34
|
-
if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:crontab]) > self.cache_timeout
|
35
|
-
@logger.debug(sprintf('invalidating [crontab] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:crontab]), self.cache_timeout))
|
36
|
-
self.deltas.delete(:crontab)
|
37
|
-
end
|
38
|
-
|
39
|
-
if self.deltas.has_key?(:crontab) and self.deltas[:crontab].has_key?(user)
|
40
|
-
@logger.debug(sprintf('using cached [crontab] from [%s]', self.cache[:crontab]))
|
41
|
-
return self.deltas[:crontab][user]
|
42
|
-
elsif self.deltas.has_key?(:crontab) and user.eql?('*')
|
43
|
-
@logger.debug(sprintf('using cached [crontab] from [%s]', self.cache[:crontab]))
|
44
|
-
return self.deltas[:crontab]
|
45
|
-
else
|
46
|
-
# noop fallthrough to gather data to cache
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
res = Hash.new
|
52
|
-
users = nil
|
53
|
-
|
54
|
-
if user.eql?('*')
|
55
|
-
users = self.get_users().keys
|
56
|
-
else
|
57
|
-
users = [user]
|
58
|
-
end
|
59
|
-
|
60
|
-
users.each do |u|
|
61
|
-
begin
|
62
|
-
raw = self.run(sprintf('crontab -u %s -l', u))
|
63
|
-
rescue RemoteExecutionError => e
|
64
|
-
# crontab throws a non-0 exit code if there is no crontab for the specified user
|
65
|
-
res[u] ||= Hash.new
|
66
|
-
next
|
67
|
-
end
|
68
|
-
|
69
|
-
raw.split("\n").each do |line|
|
70
|
-
next if line.match(/^#|^\s+$/)
|
71
|
-
elements = line.split("\s")
|
72
|
-
|
73
|
-
if elements.size < 5
|
74
|
-
# this is usually (only?) caused by ENV_VARIABLE=VALUE directives
|
75
|
-
@logger.debug(sprintf('line [%s] did not match format expectations for a crontab entry, skipping', line))
|
76
|
-
next
|
77
|
-
end
|
78
|
-
|
79
|
-
command = elements[5..elements.size].join(' ')
|
80
|
-
|
81
|
-
res[u] ||= Hash.new
|
82
|
-
|
83
|
-
if res[u][command].class.eql?(Hash)
|
84
|
-
unique = elements.join('')
|
85
|
-
command = sprintf('%s-duplicate.%s', command, unique)
|
86
|
-
@logger.info(sprintf('duplicate crontab command found, adding hash key[%s]', command))
|
87
|
-
end
|
88
|
-
|
89
|
-
res[u][command] = Hash.new
|
90
|
-
res[u][command][:minute] = elements[0]
|
91
|
-
res[u][command][:hour] = elements[1]
|
92
|
-
res[u][command][:dom] = elements[2]
|
93
|
-
res[u][command][:mon] = elements[3]
|
94
|
-
res[u][command][:dow] = elements[4]
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
if cache
|
99
|
-
@logger.debug(sprintf('caching [crontab] at [%s]', Time.now.asctime))
|
100
|
-
|
101
|
-
if ! user.eql?('*')
|
102
|
-
self.deltas[:crontab] ||= Hash.new
|
103
|
-
self.deltas[:crontab][user] ||= Hash.new
|
104
|
-
self.deltas[:crontab][user] = res[user]
|
105
|
-
else
|
106
|
-
self.deltas[:crontab] ||= Hash.new
|
107
|
-
self.deltas[:crontab] = res
|
108
|
-
end
|
109
|
-
|
110
|
-
self.cache[:crontab] = Time.now.to_i
|
111
|
-
|
112
|
-
end
|
113
|
-
|
114
|
-
return user.eql?('*') ? res : res[user]
|
115
|
-
end
|
7
|
+
# TODO use @cache_timeout to invalidate data cached here
|
116
8
|
|
9
|
+
class Rouster
|
117
10
|
##
|
118
11
|
# get_groups
|
119
12
|
#
|
@@ -127,91 +20,34 @@ class Rouster
|
|
127
20
|
#
|
128
21
|
# parameters
|
129
22
|
# * [cache] - boolean controlling whether data retrieved/parsed is cached, defaults to true
|
130
|
-
|
131
|
-
def get_groups(cache=true, deep=true)
|
132
|
-
|
23
|
+
def get_groups(cache=true)
|
133
24
|
if cache and ! self.deltas[:groups].nil?
|
134
|
-
|
135
|
-
if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:groups]) > self.cache_timeout
|
136
|
-
@logger.debug(sprintf('invalidating [groups] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:groups]), self.cache_timeout))
|
137
|
-
self.deltas.delete(:groups)
|
138
|
-
else
|
139
|
-
@logger.debug(sprintf('using cached [groups] from [%s]', self.cache[:groups]))
|
140
|
-
return self.deltas[:groups]
|
141
|
-
end
|
142
|
-
|
25
|
+
return self.deltas[:groups]
|
143
26
|
end
|
144
27
|
|
145
28
|
res = Hash.new()
|
146
29
|
|
147
|
-
|
148
|
-
:file => self.run('cat /etc/group'),
|
149
|
-
:dynamic => self.run('getent group', [0,127]),
|
150
|
-
}.each_pair do |source, raw|
|
151
|
-
|
152
|
-
raw.split("\n").each do |line|
|
153
|
-
next unless line.match(/\w+:\w+:\w+/)
|
154
|
-
|
155
|
-
data = line.split(':')
|
156
|
-
|
157
|
-
group = data[0]
|
158
|
-
gid = data[2]
|
159
|
-
|
160
|
-
# this works in some cases, deep functionality picks up the others
|
161
|
-
users = data[3].nil? ? ['NONE'] : data[3].split(',')
|
162
|
-
|
163
|
-
if res.has_key?(group)
|
164
|
-
@logger.debug(sprintf('for[%s] old GID[%s] new GID[%s]', group, gid, res[group][:users])) unless gid.eql?(res[group][:gid])
|
165
|
-
@logger.debug(sprintf('for[%s] old users[%s] new users[%s]', group, users)) unless users.eql?(res[group][:users])
|
166
|
-
end
|
167
|
-
|
168
|
-
res[group] = Hash.new() # i miss autovivification
|
169
|
-
res[group][:gid] = gid
|
170
|
-
res[group][:users] = users
|
171
|
-
res[group][:source] = source
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
|
-
groups = res
|
177
|
-
|
178
|
-
if deep
|
179
|
-
users = self.get_users(cache)
|
30
|
+
raw = self.run('cat /etc/group')
|
180
31
|
|
181
|
-
|
32
|
+
raw.split("\n").each do |line|
|
33
|
+
next unless line.match(/\w+:\w+:\w+/)
|
182
34
|
|
183
|
-
|
184
|
-
users.each_key do |user|
|
185
|
-
# iterate over each user to get their gid
|
186
|
-
gid = users[user][:gid]
|
35
|
+
data = line.split(':')
|
187
36
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
## do this more efficiently
|
194
|
-
groups.each_key do |group|
|
195
|
-
# iterate over each group to find the matching gid
|
196
|
-
if gid.eql?(groups[group][:gid])
|
197
|
-
if groups[group][:users].eql?(['NONE'])
|
198
|
-
groups[group][:users] = []
|
199
|
-
end
|
200
|
-
groups[group][:users] << user unless groups[group][:users].member?(user)
|
201
|
-
end
|
202
|
-
|
203
|
-
end
|
37
|
+
group = data[0]
|
38
|
+
gid = data[2]
|
39
|
+
users = data[3].nil? ? ['NONE'] : data[3].split(',')
|
204
40
|
|
205
|
-
|
41
|
+
res[group] = Hash.new() # i miss autovivification
|
42
|
+
res[group][:gid] = gid
|
43
|
+
res[group][:users] = users
|
206
44
|
end
|
207
45
|
|
208
46
|
if cache
|
209
|
-
|
210
|
-
self.deltas[:groups] = groups
|
211
|
-
self.cache[:groups] = Time.now.to_i
|
47
|
+
self.deltas[:groups] = res
|
212
48
|
end
|
213
49
|
|
214
|
-
|
50
|
+
res
|
215
51
|
end
|
216
52
|
|
217
53
|
##
|
@@ -230,22 +66,14 @@ class Rouster
|
|
230
66
|
#
|
231
67
|
# supported OS
|
232
68
|
# * OSX - runs `pkgutil --pkgs` and `pkgutil --pkg-info=<package>` (if deep)
|
233
|
-
# * RedHat - runs `rpm -qa
|
69
|
+
# * RedHat - runs `rpm -qa`
|
234
70
|
# * Solaris - runs `pkginfo` and `pkginfo -l <package>` (if deep)
|
235
|
-
# * Ubuntu - runs `dpkg
|
71
|
+
# * Ubuntu - runs `dpkg --get-selections` and `dpkg -s <package>` (if deep)
|
236
72
|
#
|
237
73
|
# raises InternalError if unsupported operating system
|
238
74
|
def get_packages(cache=true, deep=true)
|
239
75
|
if cache and ! self.deltas[:packages].nil?
|
240
|
-
|
241
|
-
if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:packages]) > self.cache_timeout
|
242
|
-
@logger.debug(sprintf('invalidating [packages] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:packages]), self.cache_timeout))
|
243
|
-
self.deltas.delete(:packages)
|
244
|
-
else
|
245
|
-
@logger.debug(sprintf('using cached [packages] from [%s]', self.cache[:packages]))
|
246
|
-
return self.deltas[:packages]
|
247
|
-
end
|
248
|
-
|
76
|
+
return self.deltas[:packages]
|
249
77
|
end
|
250
78
|
|
251
79
|
res = Hash.new()
|
@@ -256,93 +84,53 @@ class Rouster
|
|
256
84
|
|
257
85
|
raw = self.run('pkgutil --pkgs')
|
258
86
|
raw.split("\n").each do |line|
|
259
|
-
name = line
|
260
|
-
arch = '?'
|
261
|
-
version = '?'
|
262
87
|
|
263
88
|
if deep
|
264
89
|
# can get install time, volume and location as well
|
265
|
-
local_res = self.run(sprintf('pkgutil --pkg-info=%s',
|
266
|
-
|
267
|
-
end
|
268
|
-
|
269
|
-
if res.has_key?(name)
|
270
|
-
# different architecture of an already known package
|
271
|
-
@logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
|
272
|
-
new_element = { :version => version, :arch => arch }
|
273
|
-
res[name] = [ res[name], new_element ]
|
90
|
+
local_res = self.run(sprintf('pkgutil --pkg-info=%s', line))
|
91
|
+
local = $1 if local_res.match(/version\:\s+(.*?)$/)
|
274
92
|
else
|
275
|
-
|
93
|
+
local = '?'
|
276
94
|
end
|
277
95
|
|
96
|
+
res[line] = local
|
278
97
|
end
|
279
98
|
|
280
99
|
elsif os.eql?(:solaris)
|
281
100
|
raw = self.run('pkginfo')
|
282
101
|
raw.split("\n").each do |line|
|
283
|
-
next if line.match(/(.*?)\s+(.*?)\s(.*)$/).
|
284
|
-
name = $2
|
285
|
-
arch = '?'
|
286
|
-
version = '?'
|
102
|
+
next if line.match(/(.*?)\s+(.*?)\s(.*)$/).empty?
|
287
103
|
|
288
104
|
if deep
|
289
|
-
|
290
|
-
|
291
|
-
local_res = self.run(sprintf('pkginfo -l %s', name))
|
292
|
-
arch = $1 if local_res.match(/ARCH\:\s+(.*?)$/)
|
293
|
-
version = $1 if local_res.match(/VERSION\:\s+(.*?)$/)
|
294
|
-
rescue
|
295
|
-
arch = '?' if arch.nil?
|
296
|
-
version = '?' if arch.nil?
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
if res.has_key?(name)
|
301
|
-
# different architecture of an already known package
|
302
|
-
@logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
|
303
|
-
new_element = { :version => version, :arch => arch }
|
304
|
-
res[name] = [ res[name], new_element ]
|
105
|
+
local_res = self.run(sprintf('pkginfo -l %s', $2))
|
106
|
+
local = $1 if local_res.match(/VERSION\:\s+(.*?)$/i)
|
305
107
|
else
|
306
|
-
|
108
|
+
local = '?'
|
307
109
|
end
|
110
|
+
|
111
|
+
res[$2] = local
|
308
112
|
end
|
309
113
|
|
310
|
-
elsif os.eql?(:ubuntu)
|
311
|
-
raw = self.run(
|
114
|
+
elsif os.eql?(:ubuntu)
|
115
|
+
raw = self.run('dpkg --get-selections')
|
312
116
|
raw.split("\n").each do |line|
|
313
|
-
next if line.match(
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
if res.has_key?(name)
|
319
|
-
# different architecture of an already known package
|
320
|
-
@logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
|
321
|
-
new_element = { :version => version, :arch => arch }
|
322
|
-
res[name] = [ res[name], new_element ]
|
117
|
+
next if line.match(/^(.*?)\s/).nil?
|
118
|
+
|
119
|
+
if deep
|
120
|
+
local_res = self.run(sprintf('dpkg -s %s', $1))
|
121
|
+
local = $1 if local_res.match(/Version\:\s(.*?)$/)
|
323
122
|
else
|
324
|
-
|
123
|
+
local = '?'
|
325
124
|
end
|
326
125
|
|
126
|
+
res[$1] = local
|
327
127
|
end
|
328
128
|
|
329
|
-
elsif os.eql?(:
|
330
|
-
raw = self.run('rpm -qa
|
129
|
+
elsif os.eql?(:redhat)
|
130
|
+
raw = self.run('rpm -qa')
|
331
131
|
raw.split("\n").each do |line|
|
332
|
-
next if line.match(/(.*?)
|
333
|
-
|
334
|
-
version = $2
|
335
|
-
arch = $3
|
336
|
-
|
337
|
-
if res.has_key?(name)
|
338
|
-
# different architecture of an already known package
|
339
|
-
@logger.debug(sprintf('found package with already known name[%s], value[%s], new line[%s], turning into array', name, res[name], line))
|
340
|
-
new_element = { :version => version, :arch => arch }
|
341
|
-
res[name] = [ res[name], new_element ]
|
342
|
-
else
|
343
|
-
res[name] = { :version => version, :arch => arch }
|
344
|
-
end
|
345
|
-
|
132
|
+
next if line.match(/(.*?)-(\d*\..*)/).nil? # ht petersen.allen
|
133
|
+
res[$1] = $2
|
346
134
|
end
|
347
135
|
|
348
136
|
else
|
@@ -350,9 +138,7 @@ class Rouster
|
|
350
138
|
end
|
351
139
|
|
352
140
|
if cache
|
353
|
-
@logger.debug(sprintf('caching [packages] at [%s]', Time.now.asctime))
|
354
141
|
self.deltas[:packages] = res
|
355
|
-
self.cache[:packages] = Time.now.to_i
|
356
142
|
end
|
357
143
|
|
358
144
|
res
|
@@ -374,7 +160,7 @@ class Rouster
|
|
374
160
|
# * [cache] - boolean controlling whether data retrieved/parsed is cached, defaults to true
|
375
161
|
#
|
376
162
|
# supported OS
|
377
|
-
# * RedHat
|
163
|
+
# * RedHat - runs `netstat -ln` -- TODO will this work on other operating systems too?
|
378
164
|
#
|
379
165
|
# raises InternalError if unsupported operating system
|
380
166
|
def get_ports(cache=false)
|
@@ -382,19 +168,13 @@ class Rouster
|
|
382
168
|
# TODO improve ipv6 support
|
383
169
|
|
384
170
|
if cache and ! self.deltas[:ports].nil?
|
385
|
-
|
386
|
-
@logger.debug(sprintf('invalidating [ports] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:ports]), self.cache_timeout))
|
387
|
-
self.deltas.delete(:ports)
|
388
|
-
else
|
389
|
-
@logger.debug(sprintf('using cached [ports] from [%s]', self.cache[:ports]))
|
390
|
-
return self.deltas[:ports]
|
391
|
-
end
|
171
|
+
return self.deltas[:ports]
|
392
172
|
end
|
393
173
|
|
394
174
|
res = Hash.new()
|
395
175
|
os = self.os_type()
|
396
176
|
|
397
|
-
if os.eql?(:
|
177
|
+
if os.eql?(:redhat)
|
398
178
|
|
399
179
|
raw = self.run('netstat -ln')
|
400
180
|
|
@@ -413,14 +193,13 @@ class Rouster
|
|
413
193
|
res[protocol][port][:address][address] = state
|
414
194
|
|
415
195
|
end
|
196
|
+
|
416
197
|
else
|
417
198
|
raise InternalError.new(sprintf('unable to get port information from VM operating system[%s]', os))
|
418
199
|
end
|
419
200
|
|
420
201
|
if cache
|
421
|
-
@logger.debug(sprintf('caching [ports] at [%s]', Time.now.asctime))
|
422
202
|
self.deltas[:ports] = res
|
423
|
-
self.cache[:ports] = Time.now.to_i
|
424
203
|
end
|
425
204
|
|
426
205
|
res
|
@@ -431,306 +210,97 @@ class Rouster
|
|
431
210
|
#
|
432
211
|
# runs an OS appropriate command to gather service information, returns hash:
|
433
212
|
# {
|
434
|
-
# serviceN => mode #
|
213
|
+
# serviceN => mode # running|stopped|unsure
|
435
214
|
# }
|
436
215
|
#
|
437
216
|
# parameters
|
438
|
-
# * [cache]
|
439
|
-
# * [humanize] - boolean controlling whether data retrieved is massaged into simplified names or returned mostly as retrieved
|
440
|
-
# * [type] - symbol indicating which service controller to query, defaults to :all
|
441
|
-
# * [seed] - test hook to seed the output of service commands
|
217
|
+
# * [cache] - boolean controlling whether data retrieved/parsed is cached, defaults to true
|
442
218
|
#
|
443
|
-
# supported OS
|
444
|
-
# * OSX
|
445
|
-
# * RedHat
|
446
|
-
# * Solaris -
|
447
|
-
# * Ubuntu
|
219
|
+
# supported OS
|
220
|
+
# * OSX - runs `launchctl list`
|
221
|
+
# * RedHat - runs `/sbin/service --status-all`
|
222
|
+
# * Solaris - runs `svcs`
|
223
|
+
# * Ubuntu - runs `service --status-all`
|
448
224
|
#
|
449
|
-
#
|
450
|
-
|
451
|
-
# * OSX, Solaris and Ubuntu/Debian will only return running|stopped|unsure, the exists|installed|operational modes are RHEL/CentOS only
|
452
|
-
|
453
|
-
def get_services(cache=true, humanize=true, type=:all, seed=nil)
|
225
|
+
# raises InternalError if unsupported operating system
|
226
|
+
def get_services(cache=true)
|
454
227
|
if cache and ! self.deltas[:services].nil?
|
455
|
-
|
456
|
-
if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:services]) > self.cache_timeout
|
457
|
-
@logger.debug(sprintf('invalidating [services] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:services]), self.cache_timeout))
|
458
|
-
self.deltas.delete(:services)
|
459
|
-
else
|
460
|
-
@logger.debug(sprintf('using cached [services] from [%s]', self.cache[:services]))
|
461
|
-
return self.deltas[:services]
|
462
|
-
end
|
463
|
-
|
228
|
+
return self.deltas[:services]
|
464
229
|
end
|
465
230
|
|
466
231
|
res = Hash.new()
|
467
|
-
os = self.os_type
|
468
|
-
|
469
|
-
commands = {
|
470
|
-
:osx => {
|
471
|
-
:launchd => 'launchctl list',
|
472
|
-
},
|
473
|
-
:solaris => {
|
474
|
-
:smf => 'svcs -a',
|
475
|
-
},
|
476
|
-
|
477
|
-
# TODO we really need to implement something like osfamily
|
478
|
-
:ubuntu => {
|
479
|
-
:systemv => 'service --status-all 2>&1',
|
480
|
-
:upstart => 'initctl list',
|
481
|
-
},
|
482
|
-
:debian => {
|
483
|
-
:systemv => 'service --status-all 2>&1',
|
484
|
-
:upstart => 'initctl list',
|
485
|
-
},
|
486
|
-
:rhel => {
|
487
|
-
:systemd => 'systemctl list-units --type=service --no-pager',
|
488
|
-
:systemv => 'service --status-all',
|
489
|
-
:upstart => 'initctl list',
|
490
|
-
},
|
491
|
-
|
492
|
-
:invalid => {
|
493
|
-
:invalid => 'invalid',
|
494
|
-
},
|
495
|
-
}
|
496
|
-
|
497
|
-
if type.eql?(:all)
|
498
|
-
type = commands[os].keys
|
499
|
-
end
|
500
|
-
|
501
|
-
type = type.class.eql?(Array) ? type : [ type ]
|
502
232
|
|
503
|
-
|
233
|
+
os = self.os_type
|
504
234
|
|
505
|
-
|
506
|
-
raise ArgumentError.new(sprintf('unable to find command provider[%s] for [%s]', provider, os)) if commands[os][provider].nil?
|
235
|
+
if os.eql?(:osx)
|
507
236
|
|
508
|
-
|
509
|
-
|
510
|
-
next
|
511
|
-
end
|
237
|
+
raw = self.run('launchctl list')
|
238
|
+
raw.split("\n").each do |line|
|
239
|
+
next if line.match(/(?:\S*?)\s+(\S*?)\s+(\S*)$/).nil?
|
512
240
|
|
513
|
-
|
241
|
+
service = $2
|
242
|
+
mode = $1
|
514
243
|
|
515
|
-
|
516
|
-
|
244
|
+
if mode.match(/^\d/)
|
245
|
+
mode = 'running'
|
246
|
+
else
|
247
|
+
mode = 'stopped'
|
248
|
+
end
|
517
249
|
|
518
|
-
|
519
|
-
|
250
|
+
res[service] = mode
|
251
|
+
end
|
520
252
|
|
521
|
-
|
253
|
+
elsif os.eql?(:solaris)
|
522
254
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
service = tokens[-1]
|
527
|
-
mode = tokens[0]
|
255
|
+
raw = self.run('svcs')
|
256
|
+
raw.split("\n").each do |line|
|
257
|
+
next if line.match(/(.*?)\s+(?:.*?)\s+(.*?)$/).nil?
|
528
258
|
|
529
|
-
|
530
|
-
|
531
|
-
mode = 'running'
|
532
|
-
elsif mode.match(/-/)
|
533
|
-
mode = 'stopped'
|
534
|
-
else
|
535
|
-
next # this should handle the banner "PID Status Label"
|
536
|
-
end
|
537
|
-
end
|
259
|
+
service = $2
|
260
|
+
mode = $1
|
538
261
|
|
539
|
-
|
262
|
+
if mode.match(/online/)
|
263
|
+
mode = 'running'
|
264
|
+
elsif mode.match(/legacy_run/)
|
265
|
+
mode = 'running'
|
266
|
+
elsif mode.match(//)
|
267
|
+
mode = 'stopped'
|
540
268
|
end
|
541
269
|
|
542
|
-
|
270
|
+
res[service] = mode
|
543
271
|
|
544
|
-
|
545
|
-
next if line.match(/(.*?)\s+(?:.*?)\s+(.*?)$/).nil?
|
546
|
-
|
547
|
-
service = $2
|
548
|
-
mode = $1
|
549
|
-
|
550
|
-
if humanize
|
551
|
-
if mode.match(/^online/)
|
552
|
-
mode = 'running'
|
553
|
-
elsif mode.match(/^legacy_run/)
|
554
|
-
mode = 'running'
|
555
|
-
elsif mode.match(/^disabled/)
|
556
|
-
mode = 'stopped'
|
557
|
-
end
|
558
|
-
|
559
|
-
if service.match(/^svc:\/.*\/(.*?):.*/)
|
560
|
-
# turning 'svc:/network/cswpuppetd:default' into 'cswpuppetd'
|
561
|
-
service = $1
|
562
|
-
elsif service.match(/^lrc:\/.*?\/.*\/(.*)/)
|
563
|
-
# turning 'lrc:/etc/rcS_d/S50sk98Sol' into 'S50sk98Sol'
|
564
|
-
service = $1
|
565
|
-
end
|
566
|
-
end
|
567
|
-
|
568
|
-
res[service] = mode
|
272
|
+
end
|
569
273
|
|
570
|
-
|
274
|
+
elsif os.eql?(:ubuntu)
|
571
275
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
mode = $1
|
578
|
-
service = $2
|
579
|
-
|
580
|
-
if humanize
|
581
|
-
mode = 'stopped' if mode.match('-')
|
582
|
-
mode = 'running' if mode.match('\+')
|
583
|
-
mode = 'unsure' if mode.match('\?')
|
584
|
-
end
|
585
|
-
|
586
|
-
res[service] = mode
|
587
|
-
elsif provider.eql?(:upstart)
|
588
|
-
if line.match(/(.*?)\s.*?(.*?),/)
|
589
|
-
# tty (/dev/tty3) start/running, process 1601
|
590
|
-
# named start/running, process 8959
|
591
|
-
service = $1
|
592
|
-
mode = $2
|
593
|
-
elsif line.match(/(.*?)\s(.*)/)
|
594
|
-
# rcS stop/waiting
|
595
|
-
service = $1
|
596
|
-
mode = $2
|
597
|
-
else
|
598
|
-
@logger.warn("unable to process upstart line[#{line}], skipping")
|
599
|
-
next
|
600
|
-
end
|
601
|
-
|
602
|
-
if humanize
|
603
|
-
mode = 'stopped' if mode.match('stop/waiting')
|
604
|
-
mode = 'running' if mode.match('start/running')
|
605
|
-
mode = 'unsure' unless mode.eql?('stopped') or mode.eql?('running')
|
606
|
-
end
|
607
|
-
|
608
|
-
res[service] = mode
|
609
|
-
end
|
610
|
-
end
|
276
|
+
raw = self.run('service --status-all 2>&1')
|
277
|
+
raw.split("\n").each do |line|
|
278
|
+
next if line.match(/\[(.*?)\]\s+(.*)$/).nil?
|
279
|
+
mode = $1
|
280
|
+
service = $2
|
611
281
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
if provider.eql?(:systemv)
|
616
|
-
if humanize
|
617
|
-
if line.match(/^(\w+?)\sis\s(.*)$/)
|
618
|
-
# <service> is <state>
|
619
|
-
name = $1
|
620
|
-
state = $2
|
621
|
-
res[name] = state
|
622
|
-
|
623
|
-
if state.match(/^not/)
|
624
|
-
# this catches 'Kdump is not operational'
|
625
|
-
res[name] = 'stopped'
|
626
|
-
end
|
627
|
-
|
628
|
-
elsif line.match(/^(\w+?)\s\(pid.*?\)\sis\s(\w+)$/)
|
629
|
-
# <service> (pid <pid> [pid]) is <state>...
|
630
|
-
res[$1] = $2
|
631
|
-
elsif line.match(/^(\w+?)\sis\s(\w+)\.*$/) # not sure this is actually needed
|
632
|
-
@logger.debug('triggered supposedly unnecessary regex')
|
633
|
-
# <service> is <state>. whatever
|
634
|
-
res[$1] = $2
|
635
|
-
elsif line.match(/razor_daemon:\s(\w+).*$/)
|
636
|
-
# razor_daemon: running [pid 11325]
|
637
|
-
# razor_daemon: no instances running
|
638
|
-
res['razor_daemon'] = $1.eql?('running') ? $1 : 'stopped'
|
639
|
-
elsif line.match(/^(\w+?)\:.*?(\w+)$/)
|
640
|
-
# <service>: whatever <state>
|
641
|
-
res[$1] = $2
|
642
|
-
elsif line.match(/^(\w+?):.*?\sis\snot\srunning\.$/)
|
643
|
-
# ip6tables: Firewall is not running.
|
644
|
-
res[$1] = 'stopped'
|
645
|
-
elsif line.match(/^(\w+?)\s.*?\s(.*)$/)
|
646
|
-
# netconsole module not loaded
|
647
|
-
state = $2
|
648
|
-
res[$1] = $2.match(/not/) ? 'stopped' : 'running'
|
649
|
-
elsif line.match(/^(\w+)\s(\w+).*$/)
|
650
|
-
# <process> <state> whatever
|
651
|
-
res[$1] = $2
|
652
|
-
else
|
653
|
-
# original regex implementation, if we didn't match anything else, failover to this
|
654
|
-
next if line.match(/^([^\s:]*).*\s(\w*)(?:\.?){3}$/).nil?
|
655
|
-
res[$1] = $2
|
656
|
-
end
|
657
|
-
else
|
658
|
-
next if line.match(/^([^\s:]*).*\s(\w*)(?:\.?){3}$/).nil?
|
659
|
-
res[$1] = $2
|
660
|
-
end
|
661
|
-
elsif provider.eql?(:upstart)
|
662
|
-
|
663
|
-
if line.match(/(.*?)\s.*?(.*?),/)
|
664
|
-
# tty (/dev/tty3) start/running, process 1601
|
665
|
-
# named start/running, process 8959
|
666
|
-
service = $1
|
667
|
-
mode = $2
|
668
|
-
elsif line.match(/(.*?)\s(.*)/)
|
669
|
-
# rcS stop/waiting
|
670
|
-
service = $1
|
671
|
-
mode = $2
|
672
|
-
else
|
673
|
-
@logger.warn("unable to process upstart line[#{line}], skipping")
|
674
|
-
next
|
675
|
-
end
|
676
|
-
|
677
|
-
if humanize
|
678
|
-
mode = 'stopped' if mode.match('stop/waiting')
|
679
|
-
mode = 'running' if mode.match('start/running')
|
680
|
-
mode = 'unsure' unless mode.eql?('stopped') or mode.eql?('running')
|
681
|
-
end
|
682
|
-
|
683
|
-
res[service] = mode unless res.has_key?(service)
|
684
|
-
|
685
|
-
elsif provider.eql?(:systemd)
|
686
|
-
# UNIT LOAD ACTIVE SUB DESCRIPTION
|
687
|
-
# nfs-utils.service loaded inactive dead NFS server and client services
|
688
|
-
# crond.service loaded active running Command Scheduler
|
689
|
-
|
690
|
-
if line.match(/^\W*(.*?)\.service\s+(?:.*?)\s+(.*?)\s+(.*?)\s+(?:.*?)$/) # 5 space separated characters
|
691
|
-
service = $1
|
692
|
-
active = $2
|
693
|
-
sub = $3
|
694
|
-
|
695
|
-
if humanize
|
696
|
-
mode = sub.match('running') ? 'running' : 'stopped'
|
697
|
-
mode = 'unsure' unless mode.eql?('stopped') or mode.eql?('running')
|
698
|
-
end
|
699
|
-
|
700
|
-
res[service] = mode
|
701
|
-
else
|
702
|
-
# not logging here, there is a bunch of garbage output at the end of the output that we can't seem to suppress
|
703
|
-
next
|
704
|
-
end
|
705
|
-
|
706
|
-
end
|
282
|
+
mode = 'stopped' if mode.match('-')
|
283
|
+
mode = 'running' if mode.match('\+')
|
284
|
+
mode = 'unsure' if mode.match('\?')
|
707
285
|
|
708
|
-
|
709
|
-
else
|
710
|
-
raise InternalError.new(sprintf('unable to get service information from VM operating system[%s]', os))
|
286
|
+
res[service] = mode
|
711
287
|
end
|
712
288
|
|
289
|
+
elsif os.eql?(:redhat)
|
713
290
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
allowed_modes = %w(exists installed operational running stopped unsure)
|
720
|
-
failover_mode = 'unsure'
|
721
|
-
|
722
|
-
if humanize
|
723
|
-
res.each_pair do |k,v|
|
724
|
-
next if allowed_modes.member?(v)
|
725
|
-
@logger.debug(sprintf('replacing service[%s] status of [%s] with [%s] for uniformity', k, v, failover_mode))
|
726
|
-
res[k] = failover_mode
|
291
|
+
raw = self.run('/sbin/service --status-all')
|
292
|
+
raw.split("\n").each do |line|
|
293
|
+
# TODO tighten this up
|
294
|
+
next if line.match(/^([^\s\:]*).*\s(\w*)(?:\.?){3}$/).nil?
|
295
|
+
res[$1] = $2
|
727
296
|
end
|
297
|
+
|
298
|
+
else
|
299
|
+
raise InternalError.new(sprintf('unable to get service information from VM operating system[%s]', os))
|
728
300
|
end
|
729
301
|
|
730
302
|
if cache
|
731
|
-
@logger.debug(sprintf('caching [services] at [%s]', Time.now.asctime))
|
732
303
|
self.deltas[:services] = res
|
733
|
-
self.cache[:services] = Time.now.to_i
|
734
304
|
end
|
735
305
|
|
736
306
|
res
|
@@ -753,61 +323,32 @@ class Rouster
|
|
753
323
|
# * [cache] - boolean controlling whether data retrieved/parsed is cached, defaults to true
|
754
324
|
def get_users(cache=true)
|
755
325
|
if cache and ! self.deltas[:users].nil?
|
756
|
-
|
757
|
-
if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:users]) > self.cache_timeout
|
758
|
-
@logger.debug(sprintf('invalidating [users] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:users]), self.cache_timeout))
|
759
|
-
self.deltas.delete(:users)
|
760
|
-
else
|
761
|
-
@logger.debug(sprintf('using cached [users] from [%s]', self.cache[:users]))
|
762
|
-
return self.deltas[:users]
|
763
|
-
end
|
764
|
-
|
326
|
+
return self.deltas[:users]
|
765
327
|
end
|
766
328
|
|
767
329
|
res = Hash.new()
|
768
330
|
|
769
|
-
|
770
|
-
:file => self.run('cat /etc/passwd'),
|
771
|
-
:dynamic => self.run('getent passwd', [0,127]),
|
772
|
-
}.each do |source, raw|
|
331
|
+
raw = self.run('cat /etc/passwd')
|
773
332
|
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
user = $1
|
778
|
-
data = line.split(':')
|
779
|
-
|
780
|
-
shell = data[-1]
|
781
|
-
home = data[-2]
|
782
|
-
home_exists = self.is_dir?(data[-2])
|
783
|
-
uid = data[2]
|
784
|
-
gid = data[3]
|
785
|
-
|
786
|
-
if res.has_key?(user)
|
787
|
-
@logger.info(sprintf('for[%s] old shell[%s], new shell[%s]', res[user][:shell], shell)) unless shell.eql?(res[user][:shell])
|
788
|
-
@logger.info(sprintf('for[%s] old home[%s], new home[%s]', res[user][:home], home)) unless home.eql?(res[user][:home])
|
789
|
-
@logger.info(sprintf('for[%s] old home_exists[%s], new home_exists[%s]', res[user][:home_exists], home_exists)) unless home_exists.eql?(res[user][:home_exists])
|
790
|
-
@logger.info(sprintf('for[%s] old UID[%s], new UID[%s]', res[user][:uid], uid)) unless uid.eql?(res[user][:uid])
|
791
|
-
@logger.info(sprintf('for[%s] old GID[%s], new GID[%s]', res[user][:gid], gid)) unless gid.eql?(res[user][:gid])
|
792
|
-
end
|
333
|
+
raw.split("\n").each do |line|
|
334
|
+
next if line.match(/(\w+)(?::\w+){3,}/).nil?
|
793
335
|
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
336
|
+
user = $1
|
337
|
+
data = line.split(':')
|
338
|
+
|
339
|
+
res[user] = Hash.new()
|
340
|
+
res[user][:shell] = data[-1]
|
341
|
+
res[user][:home] = data[-2]
|
342
|
+
res[user][:home_exists] = self.is_dir?(data[-2])
|
343
|
+
res[user][:uid] = data[2]
|
344
|
+
res[user][:gid] = data[3]
|
802
345
|
end
|
803
346
|
|
804
347
|
if cache
|
805
|
-
@logger.debug(sprintf('caching [users] at [%s]', Time.now.asctime))
|
806
348
|
self.deltas[:users] = res
|
807
|
-
self.cache[:users] = Time.now.to_i
|
808
349
|
end
|
809
350
|
|
810
351
|
res
|
811
352
|
end
|
812
353
|
|
813
|
-
end
|
354
|
+
end
|