monitoringreporter 1.0.0

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.
@@ -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