rouster 0.7 → 0.41
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.
- 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
|