sensu-plugins-postgres-mrtrotl 4.3.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.
- 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
|