Skip to content

Commit baa5dee

Browse files
author
alexander popov
committed
feat: add new plugin pg_probackup. This plugin send size of backup catalog and errors in backups
1 parent 94b2a67 commit baa5dee

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
__all__ = [
22
'proc_stat', 'disk_stats', 'disk_sizes',
33
'memory', 'uptime', 'open_files', 'net', 'la'
4+
,'pg_probackup'
45
]
56

67
from . import *
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
from mamonsu.plugins.system.plugin import SystemPlugin as Plugin
2+
from mamonsu.lib.plugin import PluginDisableException
3+
import json
4+
import os
5+
import subprocess
6+
7+
8+
class PgProbackup(Plugin):
9+
os_walk_error = None
10+
block_size = 4096
11+
Interval = 5 * 60
12+
key_main = 'pg_probackup.discovery{0}'
13+
key_dir_size = 'pg_probackup.dir.size{0}'
14+
key_dir_error = 'pg_probackup.dir.error{0}'
15+
AgentPluginType = 'pg'
16+
Type = "mamonsu"
17+
18+
def set_os_walk_error(self, e):
19+
self.os_walk_error = e
20+
21+
def dir_size(self, path):
22+
self.os_walk_error = None
23+
tree = os.walk(path, onerror=self.set_os_walk_error)
24+
total_size = 0
25+
for dirpath, dirnames, filenames in tree:
26+
for file in filenames:
27+
size = os.path.getsize(os.path.join(dirpath, file))
28+
if 0 < size < self.block_size:
29+
size = round(size / self.block_size) * self.block_size + self.block_size
30+
total_size += size
31+
size = os.path.getsize(dirpath)
32+
total_size += size
33+
return total_size
34+
35+
def run(self, zbx):
36+
config_pg_probackup_path = self.plugin_config('pg_probackup_path')
37+
38+
if config_pg_probackup_path is None or config_pg_probackup_path == '':
39+
self.disable()
40+
raise PluginDisableException(
41+
"""Disable plugin and exit, because the parameter 'pg_probackup_path' in section [pgprobackup] is not
42+
set. Set this parameter if needed and restart.""")
43+
config_backup_dirs = self._plugin_config.get('backup_dirs', None)
44+
45+
if config_backup_dirs is None or config_backup_dirs == '':
46+
self.disable()
47+
raise PluginDisableException(
48+
"""Disable plugin and exit, because the parameter 'backup_dirs' in section [pgprobackup] is not set.
49+
Set this parameter if needed and restart.""")
50+
51+
backup_dirs = config_backup_dirs.split(',')
52+
dirs = []
53+
for _dir in backup_dirs:
54+
dirs.append({'{#BACKUPDIR}': _dir})
55+
56+
dir_size = self.dir_size(_dir)
57+
if self.os_walk_error:
58+
self.log.error(
59+
"Error in count size backup catalog: {backup_catalog}. Error: {error}".format(
60+
backup_catalog=_dir, error=str(self.os_walk_error)))
61+
else:
62+
zbx.send(self.key_dir_size.format('[' + _dir + ']'), dir_size)
63+
64+
command = [config_pg_probackup_path, 'show', '-B', _dir, '--format=json']
65+
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
66+
stdout, stderr = process.communicate()
67+
return_code = process.returncode
68+
if return_code != 0:
69+
self.log.error(
70+
"The command: {command} return code {return_code}. Error: {error}".format(command=command,
71+
return_code=return_code,
72+
error=stderr))
73+
continue
74+
try:
75+
result = json.loads(stdout.decode('utf-8'))
76+
except Exception as e:
77+
self.log.error('Error in convert data: {stdout} \n {e}'.format(stdout=stdout, e=e))
78+
continue
79+
80+
for instance in result:
81+
for backup in instance.get('backups', []):
82+
status = backup['status']
83+
if status in ['ERROR', 'CORRUPT', 'ORPHAN']:
84+
error = 'Backup with id: {backup_id} in instance: {instance_name} in backup catalog: ' \
85+
'{backup_catalog} has status: {status}.'.format(backup_id=backup['id'],
86+
instance_name=instance['instance'],
87+
status=status, backup_catalog=_dir)
88+
self.log.info(error)
89+
zbx.send(self.key_dir_error.format('[' + _dir + ']'), error)
90+
91+
zbx.send(self.key_main.format('[]'), zbx.json({'data': dirs}))
92+
del dirs
93+
94+
def discovery_rules(self, template):
95+
rule = {
96+
'name': 'Pg_probackup discovery',
97+
'key': self.key_main.format('[{0}]'.format(self.Macros[self.Type])),
98+
}
99+
if Plugin.old_zabbix:
100+
conditions = []
101+
rule['filter'] = '{#BACKUPDIR}:.*'
102+
else:
103+
conditions = [
104+
{
105+
'condition': [
106+
{'macro': '{#BACKUPDIR}',
107+
'value': '.*',
108+
'operator': None,
109+
'formulaid': 'A'}
110+
]
111+
}
112+
]
113+
items = [
114+
{'key': self.right_type(self.key_dir_size, var_discovery="{#BACKUPDIR},"),
115+
'name': 'Pg_probackup catalog {#BACKUPDIR}: size',
116+
'units': Plugin.UNITS.bytes,
117+
'value_type': Plugin.VALUE_TYPE.numeric_unsigned,
118+
'delay': self.plugin_config('interval')},
119+
{'key': self.right_type(self.key_dir_error, var_discovery="{#BACKUPDIR},"),
120+
'name': 'Pg_probackup catalog {#BACKUPDIR}: error',
121+
'value_type': Plugin.VALUE_TYPE.text,
122+
'delay': self.plugin_config('interval')},
123+
]
124+
graphs = [
125+
{
126+
'name': 'Pg_probackup: backup dir: {#BACKUPDIR} size',
127+
'type': 1,
128+
'items': [
129+
{'color': '00CC00',
130+
'key': self.right_type(self.key_dir_size, var_discovery="{#BACKUPDIR},")}]
131+
},
132+
]
133+
triggers = [{
134+
'name': 'Error in backup dir '
135+
'{#BACKUPDIR} (hostname={HOSTNAME} value={ITEM.LASTVALUE})',
136+
'expression': '{#TEMPLATE:pg_probackup.dir.error[{#BACKUPDIR}].strlen()}&gt;1'}
137+
]
138+
return template.discovery_rule(rule=rule, conditions=conditions, items=items, graphs=graphs, triggers=triggers)

0 commit comments

Comments
 (0)