sensu-plugins-postgres-mrtrotl 4.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +277 -0
- data/LICENSE +22 -0
- data/README.md +125 -0
- data/bin/check-postgres-alive.rb +94 -0
- data/bin/check-postgres-connections.rb +142 -0
- data/bin/check-postgres-query.rb +142 -0
- data/bin/check-postgres-replication.rb +160 -0
- data/bin/metric-postgres-connections.rb +130 -0
- data/bin/metric-postgres-dbsize.rb +105 -0
- data/bin/metric-postgres-graphite.rb +140 -0
- data/bin/metric-postgres-locks.rb +114 -0
- data/bin/metric-postgres-relation-size.rb +119 -0
- data/bin/metric-postgres-statsbgwriter.rb +113 -0
- data/bin/metric-postgres-statsdb.rb +124 -0
- data/bin/metric-postgres-statsio.rb +122 -0
- data/bin/metric-postgres-statstable.rb +124 -0
- data/bin/metric-postgres-vaccum.rb +125 -0
- data/bin/metrics-postgres-query.rb +132 -0
- data/lib/sensu-plugins-postgres/pgpass.rb +27 -0
- data/lib/sensu-plugins-postgres/pgutil.rb +15 -0
- data/lib/sensu-plugins-postgres/version.rb +11 -0
- data/lib/sensu-plugins-postgres.rb +4 -0
- metadata +330 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-postgres-query
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# This plugin queries a PostgreSQL database. It alerts when the numeric
|
9
|
+
# result hits a threshold. Can optionally alert on the number of tuples
|
10
|
+
# (rows) returned by the query.
|
11
|
+
#
|
12
|
+
# OUTPUT:
|
13
|
+
# plain-text
|
14
|
+
#
|
15
|
+
# PLATFORMS:
|
16
|
+
# Linux
|
17
|
+
#
|
18
|
+
# DEPENDENCIES:
|
19
|
+
# gem: pg
|
20
|
+
# gem: sensu-plugin
|
21
|
+
# gem: dentaku
|
22
|
+
#
|
23
|
+
# USAGE:
|
24
|
+
# check-postgres-query.rb -u db_user -p db_pass -h db_host -d db -q 'select foo from bar' -w 'value > 5' -c 'value > 10'
|
25
|
+
#
|
26
|
+
# NOTES:
|
27
|
+
#
|
28
|
+
# LICENSE:
|
29
|
+
# Copyright 2015, Eric Heydrick <eheydrick@gmail.com>
|
30
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
31
|
+
# for details.
|
32
|
+
#
|
33
|
+
|
34
|
+
require 'sensu-plugins-postgres/pgpass'
|
35
|
+
require 'sensu-plugin/check/cli'
|
36
|
+
require 'pg'
|
37
|
+
require 'dentaku'
|
38
|
+
|
39
|
+
# Check PostgresSQL Query
|
40
|
+
class CheckPostgresQuery < Sensu::Plugin::Check::CLI
|
41
|
+
option :pgpass,
|
42
|
+
description: 'Pgpass file',
|
43
|
+
short: '-f FILE',
|
44
|
+
long: '--pgpass',
|
45
|
+
default: ENV['PGPASSFILE'] || "#{ENV['HOME']}/.pgpass"
|
46
|
+
|
47
|
+
option :user,
|
48
|
+
description: 'Postgres User',
|
49
|
+
short: '-u USER',
|
50
|
+
long: '--user USER'
|
51
|
+
|
52
|
+
option :password,
|
53
|
+
description: 'Postgres Password',
|
54
|
+
short: '-p PASS',
|
55
|
+
long: '--password PASS'
|
56
|
+
|
57
|
+
option :hostname,
|
58
|
+
description: 'Hostname to login to',
|
59
|
+
short: '-h HOST',
|
60
|
+
long: '--hostname HOST'
|
61
|
+
|
62
|
+
option :port,
|
63
|
+
description: 'Database port',
|
64
|
+
short: '-P PORT',
|
65
|
+
long: '--port PORT'
|
66
|
+
|
67
|
+
option :database,
|
68
|
+
description: 'Database name',
|
69
|
+
short: '-d DB',
|
70
|
+
long: '--db DB'
|
71
|
+
|
72
|
+
option :query,
|
73
|
+
description: 'Database query to execute',
|
74
|
+
short: '-q QUERY',
|
75
|
+
long: '--query QUERY',
|
76
|
+
required: true
|
77
|
+
|
78
|
+
option :regex_pattern,
|
79
|
+
description: 'Regex pattern to match on query results and alert on if it does not match',
|
80
|
+
short: '-r REGEX',
|
81
|
+
long: '--regex-pattern REGEX'
|
82
|
+
|
83
|
+
option :check_tuples,
|
84
|
+
description: 'Check against the number of tuples (rows) returned by the query',
|
85
|
+
short: '-t',
|
86
|
+
long: '--tuples',
|
87
|
+
boolean: true,
|
88
|
+
default: false
|
89
|
+
|
90
|
+
option :warning,
|
91
|
+
description: 'Warning threshold expression',
|
92
|
+
short: '-w WARNING',
|
93
|
+
long: '--warning WARNING',
|
94
|
+
default: nil
|
95
|
+
|
96
|
+
option :critical,
|
97
|
+
description: 'Critical threshold expression',
|
98
|
+
short: '-c CRITICAL',
|
99
|
+
long: '--critical CRITICAL',
|
100
|
+
default: nil
|
101
|
+
|
102
|
+
option :timeout,
|
103
|
+
description: 'Connection timeout (seconds)',
|
104
|
+
short: '-T TIMEOUT',
|
105
|
+
long: '--timeout TIMEOUT',
|
106
|
+
default: 10,
|
107
|
+
proc: proc(&:to_i)
|
108
|
+
|
109
|
+
include Pgpass
|
110
|
+
|
111
|
+
def run
|
112
|
+
begin
|
113
|
+
pgpass
|
114
|
+
con = PG.connect(host: config[:hostname],
|
115
|
+
dbname: config[:database],
|
116
|
+
user: config[:user],
|
117
|
+
password: config[:password],
|
118
|
+
port: config[:port],
|
119
|
+
connect_timeout: config[:timeout])
|
120
|
+
res = con.exec(config[:query].to_s)
|
121
|
+
rescue PG::Error => e
|
122
|
+
unknown "Unable to query PostgreSQL: #{e.message}"
|
123
|
+
end
|
124
|
+
|
125
|
+
value = if config[:check_tuples]
|
126
|
+
res.ntuples
|
127
|
+
else
|
128
|
+
res.first.values.first.to_f
|
129
|
+
end
|
130
|
+
|
131
|
+
calc = Dentaku::Calculator.new
|
132
|
+
if config[:critical] && calc.evaluate(config[:critical], value: value)
|
133
|
+
critical "Results: #{res.values}"
|
134
|
+
elsif config[:warning] && calc.evaluate(config[:warning], value: value)
|
135
|
+
warning "Results: #{res.values}"
|
136
|
+
elsif config[:regex_pattern] && (res.getvalue(0, 0) !~ /#{config[:regex_pattern]}/)
|
137
|
+
critical "Query result #{res.getvalue(0, 0)} doesn't match configured regex #{config[:regex_pattern]}"
|
138
|
+
else
|
139
|
+
ok 'Query OK'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-postgres-replication
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
#
|
9
|
+
# This plugin checks postgresql replication lag
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# plain text
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
# gem: pg
|
20
|
+
#
|
21
|
+
# USAGE:
|
22
|
+
# ./check-postgres-replication.rb -m master_host -s slave_host -P port -d db -u db_user -p db_pass -w warn_threshold -c crit_threshold
|
23
|
+
#
|
24
|
+
# NOTES:
|
25
|
+
#
|
26
|
+
# LICENSE:
|
27
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
28
|
+
# for details.
|
29
|
+
#
|
30
|
+
|
31
|
+
require 'sensu-plugins-postgres/pgpass'
|
32
|
+
require 'sensu-plugins-postgres/pgutil'
|
33
|
+
require 'sensu-plugin/check/cli'
|
34
|
+
require 'pg'
|
35
|
+
|
36
|
+
class CheckPostgresReplicationStatus < Sensu::Plugin::Check::CLI
|
37
|
+
option :pgpass,
|
38
|
+
description: 'Pgpass file',
|
39
|
+
short: '-f FILE',
|
40
|
+
long: '--pgpass',
|
41
|
+
default: ENV['PGPASSFILE'] || "#{ENV['HOME']}/.pgpass"
|
42
|
+
|
43
|
+
option(:master_host,
|
44
|
+
short: '-m',
|
45
|
+
long: '--master-host=HOST',
|
46
|
+
required: true,
|
47
|
+
description: 'PostgreSQL master HOST')
|
48
|
+
|
49
|
+
option(:slave_host,
|
50
|
+
short: '-s',
|
51
|
+
long: '--slave-host=HOST',
|
52
|
+
required: true,
|
53
|
+
description: 'PostgreSQL slave HOST')
|
54
|
+
|
55
|
+
option(:port,
|
56
|
+
short: '-P',
|
57
|
+
long: '--port=PORT',
|
58
|
+
description: 'PostgreSQL port')
|
59
|
+
|
60
|
+
option(:database,
|
61
|
+
short: '-d',
|
62
|
+
long: '--database=NAME',
|
63
|
+
description: 'Database NAME')
|
64
|
+
|
65
|
+
option(:user,
|
66
|
+
short: '-u',
|
67
|
+
long: '--user=USER',
|
68
|
+
description: 'Database USER')
|
69
|
+
|
70
|
+
option(:password,
|
71
|
+
short: '-p',
|
72
|
+
long: '--password=PASSWORD',
|
73
|
+
description: 'Database PASSWORD')
|
74
|
+
|
75
|
+
option(:ssl,
|
76
|
+
short: '-S',
|
77
|
+
long: '--ssl',
|
78
|
+
boolean: true,
|
79
|
+
description: 'Require SSL')
|
80
|
+
|
81
|
+
option(:warn,
|
82
|
+
short: '-w',
|
83
|
+
long: '--warning=VALUE',
|
84
|
+
description: 'Warning threshold for replication lag (in MB)',
|
85
|
+
default: 900,
|
86
|
+
# #YELLOW
|
87
|
+
proc: lambda { |s| s.to_i }) # rubocop:disable Lambda
|
88
|
+
|
89
|
+
option(:crit,
|
90
|
+
short: '-c',
|
91
|
+
long: '--critical=VALUE',
|
92
|
+
description: 'Critical threshold for replication lag (in MB)',
|
93
|
+
default: 1800,
|
94
|
+
# #YELLOW
|
95
|
+
proc: lambda { |s| s.to_i }) # rubocop:disable Lambda
|
96
|
+
|
97
|
+
option(:timeout,
|
98
|
+
short: '-T TIMEOUT',
|
99
|
+
long: '--timeout=TIMEOUT',
|
100
|
+
default: 10,
|
101
|
+
description: 'Connection timeout (seconds)',
|
102
|
+
proc: proc(&:to_i))
|
103
|
+
|
104
|
+
include Pgpass
|
105
|
+
include PgUtil
|
106
|
+
|
107
|
+
def run
|
108
|
+
ssl_mode = config[:ssl] ? 'require' : 'prefer'
|
109
|
+
|
110
|
+
critical 'Master and slave cannot be the same host' if config[:master_host] == config[:slave_host]
|
111
|
+
|
112
|
+
# Establishing connection to the master
|
113
|
+
pgpass
|
114
|
+
conn_master = PG.connect(host: config[:master_host],
|
115
|
+
dbname: config[:database],
|
116
|
+
user: config[:user],
|
117
|
+
password: config[:password],
|
118
|
+
port: config[:port],
|
119
|
+
sslmode: ssl_mode,
|
120
|
+
connect_timeout: config[:timeout])
|
121
|
+
|
122
|
+
master = if check_vsn_newer_than_postgres9(conn_master)
|
123
|
+
conn_master.exec('SELECT pg_current_xlog_location()').getvalue(0, 0)
|
124
|
+
else
|
125
|
+
conn_master.exec('SELECT pg_current_wal_lsn()').getvalue(0, 0)
|
126
|
+
end
|
127
|
+
m_segbytes = conn_master.exec('SHOW wal_segment_size').getvalue(0, 0).sub(/\D+/, '').to_i << 20
|
128
|
+
conn_master.close
|
129
|
+
|
130
|
+
# Establishing connection to the slave
|
131
|
+
conn_slave = PG.connect(host: config[:slave_host],
|
132
|
+
dbname: config[:database],
|
133
|
+
user: config[:user],
|
134
|
+
password: config[:password],
|
135
|
+
port: config[:port],
|
136
|
+
sslmode: ssl_mode,
|
137
|
+
connect_timeout: config[:timeout])
|
138
|
+
|
139
|
+
slave = if check_vsn_newer_than_postgres9(conn_slave)
|
140
|
+
conn_slave.exec('SELECT pg_last_xlog_receive_location()').getvalue(0, 0)
|
141
|
+
else
|
142
|
+
conn_slave.exec('SELECT pg_last_wal_replay_lsn()').getvalue(0, 0)
|
143
|
+
end
|
144
|
+
conn_slave.close
|
145
|
+
|
146
|
+
# Computing lag
|
147
|
+
lag = compute_lag(master, slave, m_segbytes)
|
148
|
+
lag_in_mb = (lag.to_f / 1024 / 1024).abs
|
149
|
+
|
150
|
+
message = "replication delayed by #{lag_in_mb}MB :: master:#{master} slave:#{slave} m_segbytes:#{m_segbytes}"
|
151
|
+
|
152
|
+
if lag_in_mb >= config[:crit]
|
153
|
+
critical message
|
154
|
+
elsif lag_in_mb >= config[:warn]
|
155
|
+
warning message
|
156
|
+
else
|
157
|
+
ok message
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# metric-postgres-connections
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
#
|
9
|
+
# This plugin collects postgres connection metrics
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# metric data
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
# gem: pg
|
20
|
+
#
|
21
|
+
# USAGE:
|
22
|
+
# ./metric-postgres-connections.rb -u db_user -p db_pass -h db_host -d db
|
23
|
+
#
|
24
|
+
# NOTES:
|
25
|
+
#
|
26
|
+
# LICENSE:
|
27
|
+
# Copyright (c) 2012 Kwarter, Inc <platforms@kwarter.com>
|
28
|
+
# Author Gilles Devaux <gilles.devaux@gmail.com>
|
29
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
30
|
+
# for details.
|
31
|
+
#
|
32
|
+
|
33
|
+
require 'sensu-plugins-postgres/pgpass'
|
34
|
+
require 'sensu-plugin/metric/cli'
|
35
|
+
require 'pg'
|
36
|
+
require 'socket'
|
37
|
+
|
38
|
+
class PostgresStatsDBMetrics < Sensu::Plugin::Metric::CLI::Graphite
|
39
|
+
option :pgpass,
|
40
|
+
description: 'Pgpass file',
|
41
|
+
short: '-f FILE',
|
42
|
+
long: '--pgpass',
|
43
|
+
default: "#{ENV['HOME']}/.pgpass"
|
44
|
+
|
45
|
+
option :user,
|
46
|
+
description: 'Postgres User',
|
47
|
+
short: '-u USER',
|
48
|
+
long: '--user USER'
|
49
|
+
|
50
|
+
option :password,
|
51
|
+
description: 'Postgres Password',
|
52
|
+
short: '-p PASS',
|
53
|
+
long: '--password PASS'
|
54
|
+
|
55
|
+
option :hostname,
|
56
|
+
description: 'Hostname to login to',
|
57
|
+
short: '-h HOST',
|
58
|
+
long: '--hostname HOST'
|
59
|
+
|
60
|
+
option :port,
|
61
|
+
description: 'Database port',
|
62
|
+
short: '-P PORT',
|
63
|
+
long: '--port PORT'
|
64
|
+
|
65
|
+
option :database,
|
66
|
+
description: 'Database name',
|
67
|
+
short: '-d DB',
|
68
|
+
long: '--db DB'
|
69
|
+
|
70
|
+
option :scheme,
|
71
|
+
description: 'Metric naming scheme, text to prepend to $queue_name.$metric',
|
72
|
+
long: '--scheme SCHEME',
|
73
|
+
default: "#{Socket.gethostname}.postgresql"
|
74
|
+
|
75
|
+
option :timeout,
|
76
|
+
description: 'Connection timeout (seconds)',
|
77
|
+
short: '-T TIMEOUT',
|
78
|
+
long: '--timeout TIMEOUT',
|
79
|
+
default: 10,
|
80
|
+
proc: proc(&:to_i)
|
81
|
+
|
82
|
+
include Pgpass
|
83
|
+
|
84
|
+
def run
|
85
|
+
timestamp = Time.now.to_i
|
86
|
+
pgpass
|
87
|
+
con = PG.connect(host: config[:hostname],
|
88
|
+
dbname: config[:database],
|
89
|
+
user: config[:user],
|
90
|
+
password: config[:password],
|
91
|
+
port: config[:port],
|
92
|
+
connect_timeout: config[:timeout])
|
93
|
+
request = [
|
94
|
+
"select case when count(*) = 1 then 'waiting' else",
|
95
|
+
"'case when wait_event_type is null then false else true end' end as wait_col",
|
96
|
+
'from information_schema.columns',
|
97
|
+
"where table_name = 'pg_stat_activity' and table_schema = 'pg_catalog'",
|
98
|
+
"and column_name = 'waiting'"
|
99
|
+
]
|
100
|
+
wait_col = con.exec(request.join(' ')).first['wait_col']
|
101
|
+
|
102
|
+
request = [
|
103
|
+
"select count(*), #{wait_col} as waiting from pg_stat_activity",
|
104
|
+
"where datname = '#{config[:database]}' group by #{wait_col}"
|
105
|
+
]
|
106
|
+
|
107
|
+
metrics = {
|
108
|
+
active: 0,
|
109
|
+
waiting: 0,
|
110
|
+
total: 0
|
111
|
+
}
|
112
|
+
con.exec(request.join(' ')) do |result|
|
113
|
+
result.each do |row|
|
114
|
+
if row['waiting'] == 't'
|
115
|
+
metrics[:waiting] = row['count']
|
116
|
+
elsif row['waiting'] == 'f'
|
117
|
+
metrics[:active] = row['count']
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
metrics[:total] = (metrics[:waiting].to_i + metrics[:active].to_i)
|
123
|
+
|
124
|
+
metrics.each do |metric, value|
|
125
|
+
output "#{config[:scheme]}.connections.#{config[:database]}.#{metric}", value, timestamp
|
126
|
+
end
|
127
|
+
|
128
|
+
ok
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# metric-postgres-dbsize
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
#
|
9
|
+
# This plugin collects postgres database size metrics
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# metric data
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
# gem: pg
|
20
|
+
#
|
21
|
+
# USAGE:
|
22
|
+
# ./metric-postgres-dbsize.rb -u db_user -p db_pass -h db_host -d db
|
23
|
+
#
|
24
|
+
# NOTES:
|
25
|
+
#
|
26
|
+
# LICENSE:
|
27
|
+
# Copyright (c) 2012 Kwarter, Inc <platforms@kwarter.com>
|
28
|
+
# Author Gilles Devaux <gilles.devaux@gmail.com>
|
29
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
30
|
+
# for details.
|
31
|
+
#
|
32
|
+
|
33
|
+
require 'sensu-plugins-postgres/pgpass'
|
34
|
+
require 'sensu-plugin/metric/cli'
|
35
|
+
require 'pg'
|
36
|
+
require 'socket'
|
37
|
+
|
38
|
+
class PostgresStatsDBMetrics < Sensu::Plugin::Metric::CLI::Graphite
|
39
|
+
option :pgpass,
|
40
|
+
description: 'Pgpass file',
|
41
|
+
short: '-f FILE',
|
42
|
+
long: '--pgpass',
|
43
|
+
default: ENV['PGPASSFILE'] || "#{ENV['HOME']}/.pgpass"
|
44
|
+
|
45
|
+
option :user,
|
46
|
+
description: 'Postgres User',
|
47
|
+
short: '-u USER',
|
48
|
+
long: '--user USER'
|
49
|
+
|
50
|
+
option :password,
|
51
|
+
description: 'Postgres Password',
|
52
|
+
short: '-p PASS',
|
53
|
+
long: '--password PASS'
|
54
|
+
|
55
|
+
option :hostname,
|
56
|
+
description: 'Hostname to login to',
|
57
|
+
short: '-h HOST',
|
58
|
+
long: '--hostname HOST'
|
59
|
+
|
60
|
+
option :port,
|
61
|
+
description: 'Database port',
|
62
|
+
short: '-P PORT',
|
63
|
+
long: '--port PORT'
|
64
|
+
|
65
|
+
option :database,
|
66
|
+
description: 'Database name',
|
67
|
+
short: '-d DB',
|
68
|
+
long: '--db DB'
|
69
|
+
|
70
|
+
option :scheme,
|
71
|
+
description: 'Metric naming scheme, text to prepend to $queue_name.$metric',
|
72
|
+
long: '--scheme SCHEME',
|
73
|
+
default: "#{Socket.gethostname}.postgresql"
|
74
|
+
|
75
|
+
option :timeout,
|
76
|
+
description: 'Connection timeout (seconds)',
|
77
|
+
short: '-T TIMEOUT',
|
78
|
+
long: '--timeout TIMEOUT',
|
79
|
+
default: 10,
|
80
|
+
proc: proc(&:to_i)
|
81
|
+
|
82
|
+
include Pgpass
|
83
|
+
|
84
|
+
def run
|
85
|
+
timestamp = Time.now.to_i
|
86
|
+
pgpass
|
87
|
+
con = PG.connect(host: config[:hostname],
|
88
|
+
dbname: config[:database],
|
89
|
+
user: config[:user],
|
90
|
+
password: config[:password],
|
91
|
+
port: config[:port],
|
92
|
+
connect_timeout: config[:timeout])
|
93
|
+
request = [
|
94
|
+
"select pg_database_size('#{config[:database]}')"
|
95
|
+
]
|
96
|
+
|
97
|
+
con.exec(request.join(' ')) do |result|
|
98
|
+
result.each do |row|
|
99
|
+
output "#{config[:scheme]}.size.#{config[:database]}", row['pg_database_size'], timestamp
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
ok
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# metric-postgres-graphite
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
#
|
9
|
+
# This plugin collects postgres replication lag metrics
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# metric data
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
# gem: pg
|
20
|
+
#
|
21
|
+
# USAGE:
|
22
|
+
# ./metric-postgres-graphite.rb -m master_host -s slave_host -d db -u db_user -p db_pass
|
23
|
+
#
|
24
|
+
# NOTES:
|
25
|
+
#
|
26
|
+
# LICENSE:
|
27
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
28
|
+
# for details.
|
29
|
+
#
|
30
|
+
|
31
|
+
require 'sensu-plugins-postgres/pgpass'
|
32
|
+
require 'sensu-plugins-postgres/pgutil'
|
33
|
+
require 'sensu-plugin/metric/cli'
|
34
|
+
require 'pg'
|
35
|
+
require 'socket'
|
36
|
+
|
37
|
+
class CheckpostgresReplicationStatus < Sensu::Plugin::Metric::CLI::Graphite
|
38
|
+
option :pgpass,
|
39
|
+
description: 'Pgpass file',
|
40
|
+
short: '-f FILE',
|
41
|
+
long: '--pgpass',
|
42
|
+
default: ENV['PGPASSFILE'] || "#{ENV['HOME']}/.pgpass"
|
43
|
+
|
44
|
+
option :master_host,
|
45
|
+
short: '-m',
|
46
|
+
long: '--master=HOST',
|
47
|
+
description: 'PostgreSQL master HOST'
|
48
|
+
|
49
|
+
option :slave_host,
|
50
|
+
short: '-s',
|
51
|
+
long: '--slave=HOST',
|
52
|
+
description: 'PostgreSQL slave HOST',
|
53
|
+
default: 'localhost'
|
54
|
+
|
55
|
+
option :database,
|
56
|
+
short: '-d',
|
57
|
+
long: '--database=NAME',
|
58
|
+
description: 'Database NAME'
|
59
|
+
|
60
|
+
option :user,
|
61
|
+
short: '-u',
|
62
|
+
long: '--username=VALUE',
|
63
|
+
description: 'Database username'
|
64
|
+
|
65
|
+
option :password,
|
66
|
+
short: '-p',
|
67
|
+
long: '--password=VALUE',
|
68
|
+
description: 'Database password'
|
69
|
+
|
70
|
+
option :ssl,
|
71
|
+
short: '-S',
|
72
|
+
long: '--ssl',
|
73
|
+
boolean: true,
|
74
|
+
description: 'Require SSL'
|
75
|
+
|
76
|
+
option :scheme,
|
77
|
+
description: 'Metric naming scheme, text to prepend to metric',
|
78
|
+
short: '-g SCHEME',
|
79
|
+
long: '--scheme SCHEME',
|
80
|
+
default: "#{Socket.gethostname}.postgres.replication_lag"
|
81
|
+
|
82
|
+
option :port,
|
83
|
+
description: 'Database port',
|
84
|
+
short: '-P PORT',
|
85
|
+
long: '--port PORT'
|
86
|
+
|
87
|
+
option :timeout,
|
88
|
+
description: 'Connection timeout (seconds)',
|
89
|
+
short: '-T TIMEOUT',
|
90
|
+
long: '--timeout TIMEOUT',
|
91
|
+
default: 10,
|
92
|
+
proc: proc(&:to_i)
|
93
|
+
|
94
|
+
include Pgpass
|
95
|
+
include PgUtil
|
96
|
+
|
97
|
+
def run
|
98
|
+
ssl_mode = config[:ssl] ? 'require' : 'prefer'
|
99
|
+
|
100
|
+
# Establishing connections to the master
|
101
|
+
pgpass
|
102
|
+
conn_master = PG.connect(host: config[:master_host],
|
103
|
+
dbname: config[:database],
|
104
|
+
user: config[:user],
|
105
|
+
password: config[:password],
|
106
|
+
port: config[:port],
|
107
|
+
sslmode: ssl_mode,
|
108
|
+
connect_timeout: config[:timeout])
|
109
|
+
|
110
|
+
master = if check_vsn_newer_than_postgres9(conn_master)
|
111
|
+
conn_master.exec('SELECT pg_current_xlog_location()').getvalue(0, 0)
|
112
|
+
else
|
113
|
+
conn_master.exec('SELECT pg_current_wal_lsn()').getvalue(0, 0)
|
114
|
+
end
|
115
|
+
m_segbytes = conn_master.exec('SHOW wal_segment_size').getvalue(0, 0).sub(/\D+/, '').to_i << 20
|
116
|
+
conn_master.close
|
117
|
+
|
118
|
+
# Establishing connections to the slave
|
119
|
+
conn_slave = PG.connect(host: config[:slave_host],
|
120
|
+
dbname: config[:database],
|
121
|
+
user: config[:user],
|
122
|
+
password: config[:password],
|
123
|
+
port: config[:port],
|
124
|
+
sslmode: ssl_mode,
|
125
|
+
connect_timeout: config[:timeout])
|
126
|
+
|
127
|
+
slave = if check_vsn_newer_than_postgres9(conn_slave)
|
128
|
+
conn_slave.exec('SELECT pg_last_xlog_receive_location()').getvalue(0, 0)
|
129
|
+
else
|
130
|
+
conn_slave.exec('SELECT pg_last_wal_replay_lsn()').getvalue(0, 0)
|
131
|
+
end
|
132
|
+
conn_slave.close
|
133
|
+
|
134
|
+
# Compute lag
|
135
|
+
lag = compute_lag(master, slave, m_segbytes)
|
136
|
+
output config[:scheme].to_s, lag
|
137
|
+
|
138
|
+
ok
|
139
|
+
end
|
140
|
+
end
|