hostlint 0.0.1

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.
data/bin/hostlint ADDED
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env ruby
2
+ require "optparse"
3
+ require "csv"
4
+ require "logger"
5
+ require "yaml"
6
+ require "pathname"
7
+ require "pp"
8
+ require "syslog"
9
+ require "open4"
10
+ require "bunny"
11
+ require "map"
12
+
13
+ # fixme jobs support
14
+ def logd(string) $logger.add(Logger::DEBUG, string, "") if $verbose; end
15
+ def logi(string) $logger.add(Logger::INFO, string, " ") ; end
16
+ def loge(string) $logger.add(Logger::ERROR, string, "") ; end
17
+ def logw(string) $logger.add(Logger::WARN, string, " ") if $verbose; end
18
+
19
+ class Main
20
+ attr_accessor :checks
21
+
22
+ def parse_inner_opts
23
+ end
24
+ def prelude
25
+ $logger = Logger.new(STDOUT)
26
+ $logger.formatter = proc do |severity, datetime, progname, msg|
27
+ "#{severity}: #{progname}#{msg}\n"
28
+ end
29
+
30
+ optparser = OptionParser.new do |o|
31
+ o.on("-c", "--run-checks C1[,C2...]",
32
+ "run specific check(s) by name") do |arg|
33
+ @checks_filter = CSV.parse_line(arg)
34
+
35
+ end
36
+ o.on("-d", "--check-dir DIR",
37
+ "Specifies the directory of checks to run") do |arg|
38
+ @check_dir = Pathname.new(arg)
39
+ end
40
+ o.on("-f", "--config-file FILE",
41
+ "Specifies the location of the config file") do |arg|
42
+ @config_file = Pathname.new(arg)
43
+ end
44
+
45
+ # fixme re-implement
46
+ o.on("-j", "--jobs N",
47
+ "Specifies the number of jobs (checks) to run simultaneously " +
48
+ "(0 for unbounded)") do |arg|
49
+ @jobs = arg.to_i
50
+ end
51
+ o.on("-o", "--output-methods M[:OPTS]",
52
+ "Output using M (see below). Default stdout, may be specified multiple times") do |arg|
53
+ @output_methods ||= []
54
+ method, options = arg.split(":")
55
+ hsh = {}
56
+ if options
57
+ CSV.parse_line(options).map {|x| x.split("=")}.each {|k,v| hsh[k] = v}
58
+ end
59
+ @output_methods << [method, hsh]
60
+ end
61
+ o.on("-v", "--verbose", "Print debugging information") do # levels
62
+ $verbose = true
63
+ end
64
+ o.on("-u", "--user-mode",
65
+ "Allow an arbitrary user to run hostlint") do
66
+ @user_run = true
67
+ end
68
+
69
+ o.on_tail('-h', '--help',
70
+ 'print usage information and exit') do
71
+ puts o
72
+ puts <<FILE
73
+
74
+ Hostlint is like the lint program for source code, but for hosts.
75
+ Full documentation currently lives at https://github.com/whd/hostlint .
76
+
77
+ Output methods: (syntax for options is M[:opt1=val1[,opt2=val2...]])
78
+
79
+ stdout: print a concise summary of what checks ran and if they succeeded to the standard output (no options)
80
+
81
+ log: like stdout, but be more verbose by printing the stdout and stderr of each check and exit status if applicable
82
+ options: file (optional, defaults to '/var/log/hostlint.log', use '-' for standard out)
83
+
84
+ syslog: print failing checks to the system log
85
+ options: severity
86
+
87
+ yaml: output in yaml format
88
+ options: file (optional, defaults to '-', which is stdout)
89
+
90
+ amqp: send serialized results (to, say, a hostmon server) via amqp
91
+ options: host, port, user, pass, vhost, exchange=hostlint
92
+
93
+ terse: don't print anything except the summary
94
+
95
+ email: not implemented
96
+
97
+ post: not implemented
98
+ FILE
99
+ exit
100
+ end
101
+
102
+ end
103
+
104
+ optparser.parse!
105
+ abort "need to run as root (or -u)" unless @user_run || Process.euid == 0
106
+
107
+ logd "parsed command line"
108
+
109
+ @config_file ||= Pathname.new("hostlint.yml")
110
+ unless @config_file.exist? && @config_file.file? && @config_file.readable?
111
+ loge "config file #{@config_file} nonexistent or unreadable (use -f)"
112
+ abort
113
+ end
114
+
115
+ @conf = YAML.load(@config_file.read)
116
+ loge "parsing config file failed: #{@config_file}" and abort unless @conf
117
+ loge "no :main key in #{@config_file}" and abort unless @conf[:main]
118
+ @conf = Map(@conf)
119
+
120
+ # FIXME if check_dir is relative, it should resolve relative to the
121
+ # config file instead of the current working directory
122
+ # (do this without using chdir crap)
123
+ @check_dir ||= @conf[:main][:check_dir]
124
+ @host = @conf[:main][:host]
125
+ @cluster ||= @conf[:main][:colo]
126
+ unless @host && @cluster
127
+ h = %x[hostname].chomp.split(".")
128
+ @host ||= h[0]
129
+ @cluster ||= h[1]
130
+ end
131
+
132
+ if @conf[:output]
133
+ @output_methods ||= @conf[:output].map do |x|
134
+ x.respond_to?(:to_a) ? x.to_a : [x, nil]
135
+ end.map(&:first)
136
+ end
137
+ @jobs ||= 1
138
+ @output_methods ||= [["stdout", {}]]
139
+ @check_dir = Pathname.new(@check_dir)
140
+
141
+ oldpwd = Dir.pwd
142
+ Dir.chdir(@config_file.dirname)
143
+ unless @check_dir.directory? && @check_dir.readable?
144
+ loge "#{@check_dir.expand_path} not a directory or unreadable" and abort
145
+ end
146
+
147
+ @check_dir = @check_dir.realpath
148
+ Dir.chdir(oldpwd)
149
+
150
+ Check.global_info = {:host => @host, :cluster => @cluster}
151
+ logd "info:\n check_dir: #{@check_dir}" +
152
+ "\n host: #{@host}\n cluster: #{@cluster}\n jobs: #{@jobs}" +
153
+ "\n output: #{@output_methods.inspect}"
154
+ end
155
+
156
+ def check_name (f)
157
+ foo = Pathname.new(f).relative_path_from(Pathname.new(@check_dir)).to_s
158
+ foo.chomp(File.extname(f)).gsub(File::SEPARATOR, ".")
159
+ end
160
+
161
+ def theme
162
+ @checks = []
163
+ Dir.glob("#{@check_dir}/**/*").each do |f|
164
+ path = Pathname.new(f)
165
+ next unless path.file?
166
+ # FIXME add subdir support to -c (and document)
167
+ # also add a warning when checks specified with -c don't end up matching
168
+ # a file
169
+ if @checks_filter &&
170
+ !@checks_filter.find {|c| f =~ /^#{c}\.?[^\/]*$|.*\/#{c}\.?[^\/]*$/}
171
+ next
172
+ end
173
+ check = Check.new(check_name(path), path)
174
+ check.run
175
+ @checks << check
176
+ end
177
+ end
178
+
179
+ def variations
180
+ logi "SUMMARY"
181
+ if m = @checks.map {|c| c.name.length}.max
182
+ max = m + 2
183
+ end
184
+ @output_methods.each do |m, opts|
185
+ opts ||= {}
186
+ # FIXME maxl -> global_info
187
+ opts["maxl"] = max if max
188
+ method = "output_" + m.to_s
189
+ if Check.respond_to?(method)
190
+ Check.send(method, opts)
191
+ else
192
+ logw "no output method '#{m}', ignoring"
193
+ break
194
+ end
195
+ end
196
+ end
197
+
198
+ def stats
199
+ check_ok = 0
200
+ check_failed = 0
201
+ failed = 0
202
+ @checks.each do |c|
203
+ check_ok += 1 if c.check_ok?
204
+ check_failed += 1 if c.check_failed?
205
+ failed += 1 if c.failed?
206
+ end
207
+ "#{check_ok + check_failed + failed} checks total, " +
208
+ "#{check_ok} succeeded, #{check_failed} failed," +
209
+ " #{failed} failed to run properly"
210
+ end
211
+
212
+ def run
213
+ report_begin = Time.now
214
+ prelude # parse command line and configs
215
+ theme # run the checks
216
+ variations # output in various ways
217
+ report_end = Time.now
218
+
219
+ logi "hostlint check complete (#{report_end - report_begin}s)"
220
+ logi stats
221
+ end
222
+
223
+ end
224
+
225
+ class Check
226
+ @@checks = []
227
+ @@status_map = {
228
+ :CHECK_OK => 0b000000,
229
+ :CHECK_FAILED => 0b000001,
230
+ :FAILED_MISSING_PRELUDE => 0b000100,
231
+ :FAILED_STDERR_NONEMPTY => 0b001000,
232
+ :FAILED_BAD_EXIT_STATUS => 0b010000,
233
+ :FAILED_SKIPPED => 0b100000
234
+ }
235
+
236
+ @@global_info = {}
237
+ def self.global_info= (hsh)
238
+ @@global_info = hsh
239
+ end
240
+
241
+ attr_accessor :name, :exit_status, :status, :timestamp, :program, :stdout, :stderr
242
+ def initialize (name, program)
243
+ @name = name
244
+ @program = program
245
+ @status = []
246
+ @stdout, @stderr = '', ''
247
+ @@checks << self
248
+ end
249
+
250
+ def int_status
251
+ # first legitimate reason to put :| in source code I've seen
252
+ @status.map {|x| @@status_map[x]}.inject(:|)
253
+ end
254
+
255
+ # run @program, set name from first line etc. as appropriate
256
+ def run
257
+ unless @program.executable_real?
258
+ logw "#{@program} not executable, skipping"
259
+ status << :FAILED_SKIPPED
260
+ # make sure to set a timestamp
261
+ @timestamp = Time.now.to_s
262
+ return
263
+ end
264
+ regex = /^((.*):\s+)?(OK|FAIL)$/
265
+ logd "executing #{program}"
266
+
267
+ # FIXME on bad exit status, don't remove the prelude from the output stream
268
+ _status = Open4::popen4(program.to_s) do |pid, stdin, stdout, stderr|
269
+ out = stdout.read
270
+ err = stderr.read
271
+ if match = (out.lines.first||"").match(regex)
272
+ check_name = match[2]
273
+ @name = check_name if check_name
274
+ status << (match[3] == "OK" ? :CHECK_OK : :CHECK_FAILED)
275
+ out.sub!(out.lines.first, "")
276
+ else
277
+ logw "check #{@name} missing prelude"
278
+ status << :FAILED_MISSING_PRELUDE
279
+ end
280
+
281
+ if err.size > 0
282
+ logw "standard error for check #{@name} not empty"
283
+ status << :FAILED_STDERR_NONEMPTY
284
+ end
285
+ @stdout = out
286
+ @stderr = err
287
+ end
288
+
289
+ @exit_status = _status.exitstatus
290
+
291
+ if @exit_status != 0
292
+ logw "check #{@name} had nonzero exit status"
293
+ status << :FAILED_BAD_EXIT_STATUS
294
+ end
295
+ @timestamp = Time.now.to_s
296
+ end
297
+
298
+ def to_s
299
+ inspect
300
+ end
301
+
302
+ def inspect
303
+ result.inspect
304
+ end
305
+
306
+ def to_hash
307
+ @@global_info.merge({
308
+ :name => @name,
309
+ :exit_status => @exit_status,
310
+ :status => @status,
311
+ :timestamp => @timestamp,
312
+ :stdout => @stdout,
313
+ :stderr => @stderr
314
+ })
315
+ end
316
+
317
+ alias result to_hash
318
+
319
+ def check_ok?
320
+ int_status == 0
321
+ end
322
+
323
+ def check_failed?
324
+ int_status == 1
325
+ end
326
+
327
+ def failed?
328
+ int_status > 1
329
+ end
330
+
331
+ # output formats
332
+ # FIXME make instance methods (like before) and avoid duplication
333
+
334
+ # syslog
335
+ # fixme add log_healthy=false option
336
+ def self.output_syslog (opts={})
337
+ puts "output syslog"
338
+ Syslog.open unless Syslog.opened?
339
+ @@checks.each do |c|
340
+ if c.int_status > 0
341
+ # hostlint checks shouldn't really be immediately actionable
342
+ Syslog.notice "check #{c.name} failed #{c.status.inspect.gsub(":", "")}"
343
+ end
344
+ end
345
+ Syslog.close
346
+ end
347
+
348
+ # stdout
349
+ def self.output_stdout (opts={})
350
+ n = opts["maxl"]
351
+ @@checks.each do |c|
352
+ printf "%-#{n}s %s\n" % [c.name+":", c.status.inspect.gsub(":", "")]
353
+ end
354
+ end
355
+
356
+ # log, more verbose than stdout
357
+ # FIXME make formatting not suck, remove code duplication
358
+ def self.output_log (opts={})
359
+ file = opts["file"] || '/var/log/hostlint.log'
360
+ h1 = "hostlint report #{Time.now}"
361
+ header = "#{'*'*h1.length}\n#{h1}\n#{'*'*h1.length}"
362
+ if file == '-'
363
+ n = opts["maxl"]
364
+ STDOUT.puts header
365
+ @@checks.each do |c|
366
+ STDOUT.printf "%-#{n}s %s\n" % [c.name+":", c.status.inspect.gsub(":", "")]
367
+ if c.exit_status && c.exit_status != 0
368
+ STDOUT.puts " EXIT_STATUS: #{c.exit_status}"
369
+ end
370
+ unless c.stderr.empty?
371
+ STDOUT.puts " STDERR:"
372
+ STDOUT.puts c.stderr.gsub(/^/, "\t")
373
+ end
374
+ unless c.stdout.empty?
375
+ STDOUT.puts " STDOUT:"
376
+ STDOUT.puts c.stdout.gsub(/^/, "\t")
377
+ end
378
+ end
379
+ else
380
+ begin
381
+ File.open(file, "a") do |f|
382
+ f.puts header
383
+ @@checks.each do |c|
384
+ f.printf "%-#{n}s %s\n" % [c.name+":", c.status.inspect.gsub(":", "")]
385
+ if c.exit_status && c.exit_status != 0
386
+ f.puts " EXIT_STATUS: #{c.exit_status}"
387
+ end
388
+ unless c.stderr.empty?
389
+ f.puts " STDERR:"
390
+ f.puts c.stderr.gsub(/^/, "\t")
391
+ end
392
+ unless c.stdout.empty?
393
+ f.puts " STDOUT:"
394
+ f.puts c.stdout.gsub(/^/, "\t")
395
+ end
396
+ end
397
+ f.puts
398
+ end
399
+ rescue
400
+ loge "error writing to log file #{file}"
401
+ end
402
+ end
403
+ end
404
+
405
+ def self.output_terse(opts={})
406
+ end
407
+
408
+ # yaml
409
+ def self.output_yaml (opts={})
410
+ file = opts["file"] || '-'
411
+ if file == '-'
412
+ @@checks.each {|c| STDOUT.puts c.result.to_yaml}
413
+ else
414
+ begin
415
+ File.open(file, "a") do |f|
416
+ @@checks.each {|c| f.puts c.result.to_yaml}
417
+ end
418
+ rescue
419
+ loge "error writing to log file #{file}"
420
+ end
421
+ end
422
+ end
423
+
424
+ # amqp
425
+ def self.output_amqp (opts)
426
+ puts "output amqp"
427
+ connection_params = {}
428
+ connection_params[:port] = opts['port']
429
+ connection_params[:host] = opts['host']
430
+ connection_params[:user] = opts['user']
431
+ connection_params[:pass] = opts['pass']
432
+ connection_params[:vhost] = opts['vhost']
433
+ begin
434
+ amqp = Bunny.new(connection_params)
435
+ amqp.start
436
+ exchange = amqp.exchange(opts['exchange'] || 'hostlint',
437
+ :type => :topic,
438
+ :durable => true)
439
+ @@checks.each do |c|
440
+ exchange.publish c.result.to_yaml
441
+ end
442
+ amqp.close
443
+ rescue
444
+ 'amqp transport failed'
445
+ end
446
+ end
447
+ end
448
+
449
+ Main.new.run
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+
3
+ fail=""
4
+ match="BOOTPROTO=dhcp"
5
+
6
+ for file in /etc/sysconfig/networking/devices/* ; do
7
+ if [[ -f $file ]] ; then
8
+ output="$(grep -i $match $file &>/dev/null)"
9
+ if [[ $? == 0 ]]; then
10
+ fail="${fail}
11
+ ${file}"
12
+ fi
13
+ fi
14
+ done
15
+
16
+ if [[ -z "$fail" ]] ; then
17
+ echo "OK"
18
+ else
19
+ echo "network.config.dhcp: [FAIL]"
20
+ echo -n "devices configured to use ${match}:"
21
+ echo "$fail"
22
+ fi
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+
3
+ # "verify that eth0 and eth1 are bonded"
4
+ # this should likely be generalized
5
+
6
+ if [[ -d /proc/net/bonding ]] ; then
7
+ for file in /proc/net/bonding/* ; do
8
+ if grep 'eth0' $file &>/dev/null && grep 'eth1' $file &>/dev/null ; then
9
+ echo "OK"
10
+ exit 0
11
+ fi
12
+ done
13
+ fi
14
+
15
+ # fell thru
16
+ echo "FAIL
17
+ output of ifconfig:
18
+ $(ifconfig 2>/dev/null)"
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+ # requirith yum-plugin-security
3
+
4
+ output="$(yum --security check-update 2>/dev/null)"
5
+
6
+ if [[ $? == 100 ]] ; then #security updates exist!
7
+ echo "FAIL"
8
+ echo "you could try \"yum --security update\""
9
+ echo "$output" | tail -n +3
10
+ else
11
+ echo "OK"
12
+ fi
@@ -0,0 +1,12 @@
1
+ :main:
2
+ :colo: cluster1
3
+ :host: mach1
4
+ :check_dir: "../checks" # "/etc/hostlint.d"
5
+
6
+ :output:
7
+ - :log:
8
+ :file: "./log/hostlint.log"
9
+ - :amqp:
10
+ host: localhost
11
+ user: guest
12
+ pass: guest
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/bash
2
+ echo fail_stderr >&2
3
+ echo fail "bad prelude"
4
+ exit 123
5
+
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ echo 'OK
3
+ This check should EXEC_FAIL because of its exit status (even though the
4
+ prelude is correct).
5
+ '
6
+ # but exit badly
7
+ exit 1
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ echo "--INTROIBO AD ALTARE DEI. (first line doesn't match prelude)"
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ exit 1
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # EXEC_FAIL because there is output on the standard error
3
+
4
+ STDERR.puts "shoot"
5
+ puts "OK
6
+ --Come up, Kinch! Come up, you fearful jesuit!
7
+ "
8
+ exit 1
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ echo "this should fail because the prelude is missing"
@@ -0,0 +1 @@
1
+ #!/bin/bash
File without changes
@@ -0,0 +1,3 @@
1
+ #! /bin/sh
2
+ echo "exec_fail.rename.check1: OK"
3
+ exit 1
@@ -0,0 +1,3 @@
1
+ #! /bin/sh
2
+ echo "exec_fail.rename.check2: FAIL"
3
+ exit 1
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ echo 'fail.test_failure: FAIL
4
+ Buck Mulligan peeped an instant under the mirror and then covered
5
+ the bowl smartly.'
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ echo "fail.renamed_check_fail: FAIL"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env python
2
+
3
+ print """ok.python.works: OK
4
+ STATELY, PLUMP BUCK MULLIGAN CAME FROM THE STAIRHEAD, bearing a bowl of
5
+ lather on which a mirror and a razor lay crossed. A yellow dressinggown,
6
+ ungirdled, was sustained gently behind him by the mild morning air. He
7
+ held the bowl aloft and intoned:
8
+ """,
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ echo "ok.renamed_check: OK"
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ echo 'OK
4
+ --Thanks, old chap, he cried briskly. That will do nicely. Switch off the
5
+ current, will you?'
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ echo "OK"
data/tests/config.yml ADDED
@@ -0,0 +1,9 @@
1
+ # test configuration
2
+ :main:
3
+ :colo: cluster1
4
+ :host: mach1
5
+ :check_dir: "./checks" # "/etc/hostlint.d"
6
+
7
+ :output:
8
+ - :stdout
9
+
data/tests/test.rb ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # simple test wrapper, must be run from __FILE__/.
3
+ # subdirs of checks/ indicate which checks should pass, fail, and fail to run
4
+
5
+ output = %x[../bin/hostlint -u -f config.yml]
6
+ status = 0
7
+ output.each_line do |line|
8
+ next if line =~ /^INFO/
9
+ r = /^ok.*\[CHECK_OK\]$|^fail.*\[CHECK_FAILED\]$|exec_fail.*\[.*FAILED_.*$/
10
+ unless line =~ r
11
+ puts "error: line doesn't match expectations"
12
+ puts line
13
+ status = 1
14
+ end
15
+ end
16
+
17
+ if status == 0
18
+ puts "all tests pass"
19
+ end
20
+ exit status
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hostlint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Wesley Dawson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: open4
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bunny
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: map
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: hostlint client
63
+ email:
64
+ - wdawson@mozilla.com
65
+ executables:
66
+ - hostlint
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - examples/config/hostlint.yml
71
+ - examples/checks/network/config/bootproto_dhcp.sh
72
+ - examples/checks/network/ethernet_bonded.sh
73
+ - examples/checks/yum/security_updates.sh
74
+ - tests/checks/fail/fail.sh
75
+ - tests/checks/fail/rename
76
+ - tests/checks/ok/python.py
77
+ - tests/checks/ok/succeed_ok_no_body.sh
78
+ - tests/checks/ok/rename
79
+ - tests/checks/ok/succeed_ok_body.sh
80
+ - tests/checks/exec_fail/bad_output.sh
81
+ - tests/checks/exec_fail/empty_bad_exit_status.sh
82
+ - tests/checks/exec_fail/fail_stderr_bad_exit_status.rb
83
+ - tests/checks/exec_fail/bad_exit_status.sh
84
+ - tests/checks/exec_fail/rename_exec_fail1
85
+ - tests/checks/exec_fail/rename_exec_fail2
86
+ - tests/checks/exec_fail/bad_exit_prelude
87
+ - tests/checks/exec_fail/nonexec
88
+ - tests/checks/exec_fail/missing_prelude.sh
89
+ - tests/checks/exec_fail/no_output.sh
90
+ - tests/test.rb
91
+ - tests/config.yml
92
+ - bin/hostlint
93
+ homepage: https://github.com/whd/hostlint
94
+ licenses:
95
+ - Mozilla Public License (2.0)
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 1.8.24
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: hostlint -- lint for hosts
118
+ test_files: []