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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.gitlab-ci.yml +73 -0
- data/.rspec +2 -0
- data/CHANGELOG +7 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +3 -0
- data/LICENSE +202 -0
- data/README.md +60 -0
- data/Rakefile +22 -0
- data/bin/monitoringreporter +24 -0
- data/lib/monitoringreporter.rb +32 -0
- data/lib/monitoringreporter/cli.rb +114 -0
- data/lib/monitoringreporter/mysql.rb +40 -0
- data/lib/monitoringreporter/mysqllibrenms.rb +192 -0
- data/lib/monitoringreporter/reporter.rb +26 -0
- data/lib/monitoringreporter/version.rb +20 -0
- data/monitoringreporter.gemspec +63 -0
- data/spec/files/localdb.sql +1469 -0
- data/spec/files/output.json +1 -0
- data/spec/files/output.text +20 -0
- data/spec/monitoringreporter/cli_spec.rb +39 -0
- data/spec/monitoringreporter/reporter_spec.rb +67 -0
- data/spec/monitoringreporter_spec.rb +23 -0
- data/spec/spec_helper.rb +47 -0
- metadata +181 -0
@@ -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
|