Merge ~pjdc/ubuntu-mirror-charm/+git/ubuntu-mirror-charm:disk-checks into ubuntu-mirror-charm:master

Proposed by Paul Collins
Status: Merged
Approved by: Paul Collins
Approved revision: 80385747e5cc112bbfa7a7177c08522919eaddde
Merged at revision: fa87f48dff21c141b7cf2b23fac6b9baefeb320e
Proposed branch: ~pjdc/ubuntu-mirror-charm/+git/ubuntu-mirror-charm:disk-checks
Merge into: ubuntu-mirror-charm:master
Diff against target: 245 lines (+129/-36)
3 files modified
hooks/hooks.py (+65/-35)
templates/nagios-check_disk.tmpl (+1/-1)
tests/unit/test_charm.py (+63/-0)
Reviewer Review Type Date Requested Status
Thomas Cuthbert (community) Approve
Canonical IS Reviewers Pending
Review via email: mp+389138@code.launchpad.net

Commit message

deploy a disk check for each mount point from which we are serving

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Paul Collins (pjdc) wrote :
Revision history for this message
Thomas Cuthbert (tcuthbert) wrote :

LGTM just one suggestion inline plus additional comment.

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision fa87f48dff21c141b7cf2b23fac6b9baefeb320e

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/hooks/hooks.py b/hooks/hooks.py
2index ff99b06..b31d5f0 100755
3--- a/hooks/hooks.py
4+++ b/hooks/hooks.py
5@@ -56,6 +56,9 @@ apache_tls_settings = {
6 'ssl_protocol': 'ALL -SSLv2 -SSLv3 -TLSv1 -TLSv1.1',
7 }
8
9+nagios_etcdir = "/etc/nagios/nrpe.d"
10+nagios_exportdir = "/var/lib/nagios/export"
11+
12 required_pkgs = [
13 'apache2',
14 'curl', # for check-mirror.sh
15@@ -153,14 +156,11 @@ def configure_scripts(conf, hostname):
16 def file_from_template(tmpl, dest, searchList, add_header=True):
17 template_file = os.path.join(charm_dir(), "templates", tmpl)
18 t = Template(file=template_file, searchList=searchList)
19- if os.path.isfile(dest):
20- # File already exists - remove it
21- os.unlink(dest)
22- with open(dest, "w") as f:
23- if add_header:
24- f.write(juju_header())
25- f.write(str(t))
26- os.chmod(dest, 0o444)
27+ contents = ''
28+ if add_header:
29+ contents += juju_header()
30+ contents += str(t)
31+ write_file(dest, contents, perms=0o444)
32 log("CHARM: Installed template {} to {}".format(tmpl, dest))
33
34
35@@ -641,14 +641,62 @@ def configure_triggers(conf, hostname):
36 write_file(keyfile, '\n'.join(triggers.values()) + '\n')
37
38
39+def mountpoint_of(path):
40+ """Test path, and its parent directories, returning the first mount point encountered."""
41+ path = os.path.realpath(path)
42+
43+ while True:
44+ if os.path.ismount(path):
45+ return path
46+ if path == '/':
47+ break
48+ path = os.path.realpath(os.path.join(path, '..'))
49+
50+ # If we reach / and / isn't a mount point, something's wrong somewhere.
51+ raise Exception("Terminated without finding a mount point")
52+
53+
54+def collect_mountpoints(paths):
55+ mountpoints = []
56+ for path in paths:
57+ mountpoints.append(mountpoint_of(path))
58+ return set(mountpoints)
59+
60+
61+def generate_disk_check(conf, path):
62+ mount_name = path.replace('/', '_').strip('_')
63+
64+ file_from_template(
65+ "nagios-check_disk.tmpl",
66+ os.path.join(nagios_etcdir, "check_disk_{mount_name}.cfg".format(mount_name=mount_name)),
67+ {
68+ "mount_name": mount_name,
69+ "space_warn": conf.disk_space_warn(),
70+ "space_crit": conf.disk_space_crit(),
71+ "inode_crit": conf.disk_inode_crit(),
72+ "path": path,
73+ }
74+ )
75+ file_from_template(
76+ "nagios-config.tmpl",
77+ os.path.join(nagios_exportdir, "service__{hostname}_disk_{mount_name}.cfg".format(
78+ hostname=conf.nagios_hostname(), mount_name=mount_name)),
79+ {
80+ "use": "active-service",
81+ "nagios_hostname": conf.nagios_hostname(),
82+ "description": "Disk space on {}".format(path),
83+ "check_command": "check_nrpe!check_disk_{}".format(mount_name),
84+ "servicegroup": conf.nagios_servicegroup(),
85+ }
86+ )
87+ log("CHARM: Adding disk check for {}".format(path))
88+
89+
90 def configure_nrpe(conf, hostname): # noqa: C901
91 if not conf.nagios_relation_ok():
92 log("CHARM: Nagios relation not established - not configuring nrpe")
93 return
94
95- nagios_etcdir = "/etc/nagios/nrpe.d"
96- nagios_exportdir = "/var/lib/nagios/export"
97-
98 role_checks = {
99 "check_virthost": {"desc": "Check apache virthost"},
100 "check_mirror": {"desc": "GPG signature via HTTP"},
101@@ -668,6 +716,7 @@ def configure_nrpe(conf, hostname): # noqa: C901
102 wanted_checks = []
103 # Add Nagios role checks
104 roles = conf.roles()
105+ paths = []
106 if hostname in roles:
107 tmpl_data = {}
108 for role in roles[hostname]:
109@@ -677,6 +726,7 @@ def configure_nrpe(conf, hostname): # noqa: C901
110 log("CHARM: Missing details - skipping {}".format(role))
111 continue
112
113+ paths.append(mirror['path'])
114 tmpl_data["name"] = mirror["name"]
115 role_config = get_role_config(roles, hostname, role)
116 addresses = role_config.get('addresses', [])
117@@ -751,30 +801,10 @@ def configure_nrpe(conf, hostname): # noqa: C901
118 except OSError:
119 log("CHARM: Unable to delete nrpe check {}".format(checkpath))
120
121- # Add disk checks (if /srv is a separate partition)
122- with open("/proc/mounts", "r") as mounts:
123- for line in mounts:
124- if line.split(" ")[1] == "/srv":
125- checkfile = "check_disk_srv.cfg"
126- checkpath = os.path.join(nagios_etcdir, checkfile)
127- tmpl_data = {}
128- tmpl_data["space_warn"] = conf.disk_space_warn()
129- tmpl_data["space_crit"] = conf.disk_space_crit()
130- tmpl_data["inode_crit"] = conf.disk_inode_crit()
131- tmpl_data["partition"] = "srv"
132- file_from_template("nagios-check_disk.tmpl", checkpath, tmpl_data)
133- configpath = os.path.join(nagios_exportdir, "service__%s_disk_srv.cfg" % (conf.nagios_hostname()))
134- tmpl_data = {}
135- tmpl_data["use"] = "active-service"
136- tmpl_data["nagios_hostname"] = conf.nagios_hostname()
137- tmpl_data["description"] = "Disk space on /srv"
138- tmpl_data["check_command"] = "check_nrpe!check_disk_srv"
139- tmpl_data["servicegroup"] = conf.nagios_servicegroup()
140- tmplfile = "nagios-config.tmpl"
141- file_from_template(tmplfile, configpath, tmpl_data)
142-
143- log("CHARM: Adding checkfile {}".format(checkfile))
144- break
145+ # Add disk checks
146+ paths_to_check = [path for path in collect_mountpoints(paths) if path != '/']
147+ for path in paths_to_check:
148+ generate_disk_check(conf, path)
149
150 # RT#79518: Add prompt-critical check for all disks; taken from puppet.
151 check_all_disks = juju_header()
152diff --git a/templates/nagios-check_disk.tmpl b/templates/nagios-check_disk.tmpl
153index 4983e4f..1ed2a3e 100644
154--- a/templates/nagios-check_disk.tmpl
155+++ b/templates/nagios-check_disk.tmpl
156@@ -1 +1 @@
157-command[check_disk_${partition}]=/usr/lib/nagios/plugins/check_disk -u GB -w ${space_warn} -c ${space_crit} -K ${inode_crit} -p /${partition}
158+command[check_disk_${mount_name}]=/usr/lib/nagios/plugins/check_disk -u GB -w ${space_warn} -c ${space_crit} -K ${inode_crit} -p ${path}
159diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
160index 02fb4f2..a8a2fb8 100644
161--- a/tests/unit/test_charm.py
162+++ b/tests/unit/test_charm.py
163@@ -1,4 +1,6 @@
164 import json
165+import os
166+import pytest
167 import unittest
168
169 from base64 import b64encode
170@@ -10,10 +12,13 @@ import hooks # noqa: F401; for mockery
171
172 from hooks import (
173 SSH_TRIGGER_TEMPLATE,
174+ collect_mountpoints,
175 configure_triggers,
176 extract_ssh_public_key,
177+ generate_disk_check,
178 make_triggers,
179 merge_shared_key_triggers,
180+ mountpoint_of,
181 )
182
183 from utils import (
184@@ -119,3 +124,61 @@ class TestUbuntuMirrorCharm(unittest.TestCase):
185 _isdir.return_value = True
186 configure_triggers(conf, 'localhost')
187 _write_file.assert_called_with(_get_ssh_keyfile.return_value, MERGEABLE_TRIGGERS_SSH_AUTHORIZED_KEYS)
188+
189+ def test_mountpoint_of_root(self):
190+ """If the mountpoint of / isn't /, we're running in a weird environment."""
191+ self.assertEqual(mountpoint_of('/'), '/')
192+
193+ def test_mountpoint_of_bin(self):
194+ """If the mountpoint of /bin isn't /, we're running in a weird environment."""
195+ self.assertEqual(mountpoint_of('/bin'), '/')
196+
197+ def test_mountpoint_of_proc_self_fd(self):
198+ """Linux-specific because it's hard to think of a more general but equally complex example."""
199+ self.assertEqual(mountpoint_of('/proc/self/fd'), '/proc')
200+
201+ def test_collect_mountpoints(self):
202+ """Given the tests for mountpoint_of, we can test collect_mountpoints like this."""
203+ self.assertEqual(collect_mountpoints(['/', '/bin', '/proc/self/fd']), {'/', '/proc'})
204+
205+ # Cheetah warns about using the Python version of NameMapper, so
206+ # we ignore it here. Matching more closely by the message doesn't
207+ # seem to work, probably because it begins with a newline and
208+ # Python's warnings matching code anchors the message regexp.
209+ @pytest.mark.filterwarnings("ignore::UserWarning:Cheetah")
210+ @patch('hooks.charm_dir')
211+ @patch('hooks.write_file')
212+ @patch('Config.config')
213+ def test_generate_disk_check(self, _config, _write_file, _charm_dir):
214+ _charm_dir.return_value = os.getcwd()
215+ config = CallableDict(self.default_config)
216+ _config.side_effect = config # i.e. we want our CallableDict to supply the charm config
217+ conf = Config.Config()
218+ conf._relation_data = {
219+ 'nagios_hostname': 'localhost',
220+ 'nagios_host_context': 'fake-archive-mirror',
221+ }
222+ generate_disk_check(conf, '/srv2')
223+ self.assertEqual(_write_file.call_count, 2)
224+ _write_file.assert_any_call(
225+ '/etc/nagios/nrpe.d/check_disk_srv2.cfg',
226+ '#-------------------------------------------------#\n'
227+ '# This file is Juju managed - do not edit by hand #\n'
228+ '#-------------------------------------------------#\n'
229+ 'command[check_disk_srv2]=/usr/lib/nagios/plugins/check_disk -u GB -w 25% -c 20% -K 5% -p /srv2\n',
230+ perms=0o444,
231+ )
232+ _write_file.assert_any_call(
233+ '/var/lib/nagios/export/service__localhost_disk_srv2.cfg',
234+ '#-------------------------------------------------#\n'
235+ '# This file is Juju managed - do not edit by hand #\n'
236+ '#-------------------------------------------------#\n'
237+ 'define service {\n'
238+ ' use active-service\n'
239+ ' host_name localhost\n'
240+ ' service_description Disk space on /srv2\n'
241+ ' check_command check_nrpe!check_disk_srv2\n'
242+ ' servicegroups fake-archive-mirror-ubuntu-mirror\n'
243+ '}\n',
244+ perms=0o444,
245+ )

Subscribers

People subscribed via source and target branches