monitoringreporter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ # MonitoringReporter class
19
+ module MonitoringReporter
20
+ class << self
21
+ Dir[File.join(File.dirname(__FILE__), '*', '*.rb')].each do |file|
22
+ require file
23
+ end
24
+
25
+ def start(args = ARGV)
26
+ MonitoringReporter::CLI.start(args)
27
+ rescue => e
28
+ $stderr.puts e
29
+ exit(1)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'thor'
19
+
20
+ module MonitoringReporter
21
+ # Simple CLI for monitoringreporter
22
+ class CLI < Thor
23
+ desc 'version', 'Print monitoringreporter current version'
24
+ def version
25
+ puts "MonitoringReporter version #{MonitoringReporter::VERSION}"
26
+ end
27
+
28
+ desc('report [options]',
29
+ 'Send a request to the DB to get the requested data.' \
30
+ ' DATA: may be one of []')
31
+ option(
32
+ :baseurl,
33
+ aliases: ['-b'],
34
+ desc: 'Monitoring tool base url.'
35
+ )
36
+ option(
37
+ :host,
38
+ aliases: ['-h'],
39
+ desc: 'Database host to request.'
40
+ )
41
+ option(
42
+ :port,
43
+ aliases: ['-P'],
44
+ default: '3306',
45
+ desc: 'Database port to request.'
46
+ )
47
+ option(
48
+ :socket,
49
+ aliases: ['-x'],
50
+ desc: 'Database socket to request.'
51
+ )
52
+ option(
53
+ :database,
54
+ aliases: ['-d'],
55
+ desc: 'Database to request.'
56
+ )
57
+ option(
58
+ :user,
59
+ aliases: ['-u'],
60
+ desc: 'User name in order to login. See also password option.'
61
+ )
62
+ option(
63
+ :password,
64
+ aliases: ['-p'],
65
+ desc: 'User password in order to login. See also user option.'
66
+ )
67
+ option(
68
+ :output,
69
+ aliases: ['-o'],
70
+ desc: 'Output file.'
71
+ )
72
+ option(
73
+ :periodicity,
74
+ aliases: ['-t'],
75
+ type: :numeric,
76
+ default: 43_200,
77
+ desc: 'Report on the last T times.'
78
+ )
79
+ option(
80
+ :source,
81
+ aliases: ['-S'],
82
+ default: 'librenms',
83
+ enum: %w[librenms],
84
+ desc: 'Select the source db type.'
85
+ )
86
+ option(
87
+ :format,
88
+ aliases: ['-f'],
89
+ default: 'text',
90
+ enum: %w[text json],
91
+ desc: 'Select the output format type.'
92
+ )
93
+ option(
94
+ :quiet,
95
+ aliases: ['-q'],
96
+ type: :boolean,
97
+ default: false,
98
+ desc: 'Silently do the job'
99
+ )
100
+ option(
101
+ :simulation,
102
+ aliases: ['-s'],
103
+ type: :boolean,
104
+ default: false,
105
+ desc: 'Simulation mode. Do nothing'
106
+ )
107
+ def report
108
+ opts = options.dup
109
+ rep = MonitoringReporter::Reporter.new.send(opts['source'], opts) \
110
+ unless opts['simulation']
111
+ rep.report unless opts['simulation']
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'mysql'
19
+
20
+ module MonitoringReporter
21
+ # This is mysql class to request the databse.
22
+ class MySQL
23
+ # Class constructor method
24
+ def initialize(opts)
25
+ @opts = opts
26
+ @my = Mysql.connect(
27
+ opts['host'],
28
+ opts['user'],
29
+ opts['password'],
30
+ opts['database'],
31
+ opts['port'],
32
+ opts['socket']
33
+ )
34
+ end
35
+
36
+ def query(query)
37
+ @my.query(query).to_a
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'monitoringreporter/mysql'
19
+ require 'json'
20
+
21
+ module MonitoringReporter
22
+ # This is librenms class to request the various datas.
23
+ class MysqlLibreNMS < MySQL # rubocop:disable Metrics/ClassLength
24
+ # Print the report.
25
+ def report
26
+ period = Time.now.to_i - @opts['periodicity']
27
+ results = send_query(period)
28
+ data = format_data(results)
29
+ print_to_output(data)
30
+ end
31
+
32
+ # Print the result to the selected output
33
+ def print_to_output(data)
34
+ if @opts['output'].nil?
35
+ puts data
36
+ else
37
+ File.write(@opts['output'], data)
38
+ end
39
+ end
40
+
41
+ # Send queries to the database
42
+ def send_query(since_period)
43
+ results = {}
44
+ results['hosts'] = id_to_host
45
+ results['total'] = total_host_count
46
+ results['up'] = up_host_count
47
+ results['down'] = down_host_count
48
+ results['average'] = average(since_period)
49
+ results['longests'] = longest(since_period)
50
+ results['missings'] = missing(since_period)
51
+ results
52
+ end
53
+
54
+ # Get device_id and hostname matching
55
+ def id_to_host
56
+ q = 'SELECT hostname,device_id FROM devices;'
57
+ Hash[query(q)]
58
+ end
59
+
60
+ # Get the total number of hosts.
61
+ def total_host_count
62
+ q = 'SELECT COUNT(*) FROM devices;'
63
+ query(q)[0][0].to_i
64
+ end
65
+
66
+ # Get the number of hosts up.
67
+ def up_host_count
68
+ q = 'SELECT COUNT(*) FROM devices WHERE status = 1;'
69
+ query(q)[0][0].to_i
70
+ end
71
+
72
+ # Get the number of hosts down.
73
+ def down_host_count
74
+ q = 'SELECT COUNT(*) FROM devices WHERE status = 0;'
75
+ query(q)[0][0].to_i
76
+ end
77
+
78
+ # Get the average polling duration.
79
+ def average(since_period)
80
+ q = 'SELECT AVG(duration) FROM perf_times '\
81
+ "WHERE start > '#{since_period}' AND perf_times.type = 'poll';"
82
+ query(q)[0][0].to_i
83
+ end
84
+
85
+ # Get the 5 longest poller.
86
+ def longest(since_period)
87
+ q = 'SELECT hostname,MAX(duration) AS drt FROM devices ' \
88
+ 'LEFT JOIN perf_times ON devices.device_id=perf_times.doing ' \
89
+ "WHERE start > '#{since_period}' AND perf_times.type = 'poll' " \
90
+ 'GROUP BY doing ORDER BY drt DESC LIMIT 5;'
91
+ query(q)
92
+ end
93
+
94
+ # Get the missing polling count.
95
+ def missing(since_period)
96
+ q = 'SELECT hostname,start FROM devices LEFT JOIN perf_times ' \
97
+ 'ON devices.device_id=perf_times.doing ' \
98
+ "WHERE start > '#{since_period}' AND perf_times.type = 'poll' " \
99
+ 'ORDER BY hostname DESC, start ASC;'
100
+ search_for_missing(query(q))
101
+ end
102
+
103
+ # Process the data to find the missing polling.
104
+ def search_for_missing(data_array)
105
+ miss_hash = {}
106
+ array_to_hash(data_array).each_pair do |key, array|
107
+ ref = array[0].to_i
108
+ miss_hash[key] = 0
109
+ array.each do |val|
110
+ miss_hash[key] += (val.to_i - ref) / 420 unless val.to_i < ref + 420
111
+ ref = val.to_i
112
+ end
113
+ end
114
+ miss_hash
115
+ end
116
+
117
+ # Convert an array to hash.
118
+ def array_to_hash(array)
119
+ myhash = {}
120
+ array.each do |pair|
121
+ myhash[pair[0]] = [] if myhash[pair[0]].nil?
122
+ myhash[pair[0]].push(pair[1])
123
+ end
124
+ myhash
125
+ end
126
+
127
+ # Format output to selected datatype.
128
+ def format_data(results)
129
+ case @opts['format'].downcase
130
+ when 'text' then format_text(results)
131
+ when 'json' then format_json(results)
132
+ end
133
+ end
134
+
135
+ # Format to json output
136
+ def format_json(res)
137
+ {
138
+ 'hosts' => {
139
+ 'up' => res['up'], 'down' => res['down'], 'total' => res['total']
140
+ },
141
+ 'polling' => {
142
+ 'average' => res['average'], 'longest' => Hash[res['longests']],
143
+ 'missing' => res['missings'].delete_if { |_, v| v.zero? }
144
+ .sort_by { |_, v| v }.reverse
145
+ }
146
+ }.to_json
147
+ end
148
+
149
+ # Format to text output.
150
+ def format_text(res)
151
+ out = format_text_hosts(res['up'], res['down'], res['total'])
152
+ out += format_text_average(res['average'])
153
+ out += format_text_longest(res['longests'])
154
+ out += format_text_missing(res['missings'], res['hosts'])
155
+ out
156
+ end
157
+
158
+ # Format the text output for host status.
159
+ def format_text_hosts(up, down, total)
160
+ "Number of hosts UP: #{up}/#{total} " \
161
+ "(#{(up.to_f / total * 100).round(2)}%)\n" \
162
+ "Number of hosts DOWN: #{down}/#{total} " \
163
+ "(#{(down.to_f / total * 100).round(2)}%)\n" \
164
+ end
165
+
166
+ # Format the text output for average polling.
167
+ def format_text_average(value)
168
+ "Average polling duration: #{value}\n"
169
+ end
170
+
171
+ # Format the text output for longest polling list.
172
+ def format_text_longest(values)
173
+ out = "Longest pollers:\n"
174
+ values.each { |v| out += "- #{v[0]} (#{v[1]} seconds)\n" }
175
+ out
176
+ end
177
+
178
+ # Format the text output for missing polling list.
179
+ def format_text_missing(values, hosts)
180
+ out = "Missing poll count per host (estimated):\n"
181
+ base = "#{@opts['baseurl'].sub(%r{/^(.*)\/$/}, '\1')}/device/device=" \
182
+ unless @opts['baseurl'].nil?
183
+ Hash[values.sort_by { |_, v| v }.reverse].each_pair do |key, value|
184
+ next if value.zero?
185
+ out += "- #{key}: #{value}"
186
+ out += " (#{base}#{hosts[key]})" unless base.nil?
187
+ out += "\n"
188
+ end
189
+ out
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module MonitoringReporter
19
+ # This is a wrapper class used o call the selected database.
20
+ class Reporter
21
+ # MonitoringReporter::LibreNMS wrapper
22
+ def librenms(opts)
23
+ MonitoringReporter::MysqlLibreNMS.new(opts)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module MonitoringReporter
19
+ VERSION = '1.0.0'.freeze
20
+ end
@@ -0,0 +1,63 @@
1
+ #
2
+ # Copyright (c) 2017 Richard Delaplace, Vente-Privee.Com
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ lib = File.expand_path('../lib', __FILE__)
18
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
19
+ require 'monitoringreporter/version'
20
+
21
+ dev_deps = {
22
+ 'bundler' => '~> 1.12',
23
+ 'rspec' => '~> 3.5',
24
+ 'rake' => '~> 11.2',
25
+ 'rubocop' => '~> 0.41',
26
+ 'webmock' => '~> 1.23',
27
+ 'simplecov' => '~> 0.12'
28
+ }
29
+
30
+ deps = {
31
+ 'ruby-mysql' => '~> 2.9',
32
+ 'thor' => '~> 0.19'
33
+ }
34
+
35
+ Gem::Specification.new do |s|
36
+ s.name = 'monitoringreporter'
37
+ s.version = MonitoringReporter::VERSION
38
+ s.authors = ['Richard Delaplace']
39
+ s.email = 'rdelaplace@vente-privee.com'
40
+ s.license = 'Apache-2.0'
41
+
42
+ s.summary = 'Tool to request monitoring tool database.'
43
+ s.description = 'MonitoringReporter is a simple tool to request monitoring' \
44
+ ' tool data base and extract some information useful to be reported'
45
+ s.homepage = 'https://github.com/vp-noc/monitoringreporter'
46
+
47
+ s.files = `git ls-files`.lines.map(&:chomp)
48
+ s.bindir = 'bin'
49
+ s.executables = `git ls-files bin/*`.lines.map do |exe|
50
+ File.basename(exe.chomp)
51
+ end
52
+ s.require_paths = ['lib']
53
+
54
+ s.required_ruby_version = '>= 1.9.3'
55
+
56
+ dev_deps.each_pair do |deps_gem, deps_version|
57
+ s.add_development_dependency deps_gem, deps_version
58
+ end
59
+
60
+ deps.each_pair do |deps_gem, deps_version|
61
+ s.add_dependency deps_gem, deps_version
62
+ end
63
+ end