497 lines
20 KiB
Python
497 lines
20 KiB
Python
import os
|
|
import yaml
|
|
import subprocess
|
|
|
|
from testtools import TestCase
|
|
from mock import patch, call, MagicMock
|
|
|
|
from charmhelpers.contrib.charmsupport import nrpe
|
|
from charmhelpers.core import host
|
|
|
|
|
|
class NRPEBaseTestCase(TestCase):
|
|
patches = {
|
|
'config': {'object': nrpe},
|
|
'copy2': {'object': nrpe.shutil},
|
|
'log': {'object': nrpe},
|
|
'getpwnam': {'object': nrpe.pwd},
|
|
'getgrnam': {'object': nrpe.grp},
|
|
'glob': {'object': nrpe.glob},
|
|
'mkdir': {'object': os},
|
|
'chown': {'object': os},
|
|
'chmod': {'object': os},
|
|
'exists': {'object': os.path},
|
|
'listdir': {'object': os},
|
|
'remove': {'object': os},
|
|
'open': {'object': nrpe, 'create': True},
|
|
'isfile': {'object': os.path},
|
|
'isdir': {'object': os.path},
|
|
'call': {'object': subprocess},
|
|
'relation_get': {'object': nrpe},
|
|
'relation_ids': {'object': nrpe},
|
|
'relation_set': {'object': nrpe},
|
|
'relations_of_type': {'object': nrpe},
|
|
'service': {'object': nrpe},
|
|
'init_is_systemd': {'object': host},
|
|
}
|
|
|
|
def setUp(self):
|
|
super(NRPEBaseTestCase, self).setUp()
|
|
self.patched = {}
|
|
# Mock the universe.
|
|
for attr, data in self.patches.items():
|
|
create = data.get('create', False)
|
|
patcher = patch.object(data['object'], attr, create=create)
|
|
self.patched[attr] = patcher.start()
|
|
self.addCleanup(patcher.stop)
|
|
env_patcher = patch.dict('os.environ',
|
|
{'JUJU_UNIT_NAME': 'testunit',
|
|
'CHARM_DIR': '/usr/lib/test_charm_dir'})
|
|
env_patcher.start()
|
|
self.addCleanup(env_patcher.stop)
|
|
|
|
def check_call_counts(self, **kwargs):
|
|
for attr, expected in kwargs.items():
|
|
patcher = self.patched[attr]
|
|
self.assertEqual(expected, patcher.call_count, attr)
|
|
|
|
|
|
class NRPETestCase(NRPEBaseTestCase):
|
|
|
|
def test_init_gets_config(self):
|
|
self.patched['config'].return_value = {'nagios_context': 'testctx',
|
|
'nagios_servicegroups': 'testsgrps'}
|
|
|
|
checker = nrpe.NRPE()
|
|
|
|
self.assertEqual('testctx', checker.nagios_context)
|
|
self.assertEqual('testsgrps', checker.nagios_servicegroups)
|
|
self.assertEqual('testunit', checker.unit_name)
|
|
self.assertEqual('testctx-testunit', checker.hostname)
|
|
self.check_call_counts(config=1)
|
|
|
|
def test_init_hostname(self):
|
|
"""Test that the hostname parameter is correctly set"""
|
|
checker = nrpe.NRPE()
|
|
self.assertEqual(checker.hostname,
|
|
"{}-{}".format(checker.nagios_context,
|
|
checker.unit_name))
|
|
hostname = "test.host"
|
|
checker = nrpe.NRPE(hostname=hostname)
|
|
self.assertEqual(checker.hostname, hostname)
|
|
|
|
def test_default_servicegroup(self):
|
|
"""Test that nagios_servicegroups gets set to the default if omitted"""
|
|
self.patched['config'].return_value = {'nagios_context': 'testctx'}
|
|
checker = nrpe.NRPE()
|
|
self.assertEqual(checker.nagios_servicegroups, 'testctx')
|
|
|
|
def test_no_nagios_installed_bails(self):
|
|
self.patched['config'].return_value = {'nagios_context': 'test',
|
|
'nagios_servicegroups': ''}
|
|
self.patched['getgrnam'].side_effect = KeyError
|
|
checker = nrpe.NRPE()
|
|
|
|
self.assertEqual(None, checker.write())
|
|
|
|
expected = 'Nagios user not set up, nrpe checks not updated'
|
|
self.patched['log'].assert_called_with(expected)
|
|
self.check_call_counts(log=2, config=1, getpwnam=1, getgrnam=1)
|
|
|
|
def test_write_no_checker(self):
|
|
self.patched['config'].return_value = {'nagios_context': 'test',
|
|
'nagios_servicegroups': ''}
|
|
self.patched['exists'].return_value = True
|
|
checker = nrpe.NRPE()
|
|
|
|
self.assertEqual(None, checker.write())
|
|
|
|
self.check_call_counts(config=1, getpwnam=1, getgrnam=1, exists=1)
|
|
|
|
def test_write_restarts_service(self):
|
|
self.patched['config'].return_value = {'nagios_context': 'test',
|
|
'nagios_servicegroups': ''}
|
|
self.patched['exists'].return_value = True
|
|
checker = nrpe.NRPE()
|
|
|
|
self.assertEqual(None, checker.write())
|
|
|
|
self.patched['service'].assert_called_with('restart', 'nagios-nrpe-server')
|
|
self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
|
|
exists=1, service=1)
|
|
|
|
def test_update_nrpe(self):
|
|
self.patched['config'].return_value = {'nagios_context': 'a',
|
|
'nagios_servicegroups': ''}
|
|
self.patched['exists'].return_value = True
|
|
self.patched['relation_get'].return_value = {
|
|
'egress-subnets': '10.66.111.24/32',
|
|
'ingress-address': '10.66.111.24',
|
|
'private-address': '10.66.111.24'
|
|
}
|
|
|
|
def _rels(rname):
|
|
relations = {
|
|
'local-monitors': 'local-monitors:1',
|
|
'nrpe-external-master': 'nrpe-external-master:2',
|
|
}
|
|
return [relations[rname]]
|
|
self.patched['relation_ids'].side_effect = _rels
|
|
|
|
checker = nrpe.NRPE()
|
|
checker.add_check(shortname="myservice",
|
|
description="Check MyService",
|
|
check_cmd="check_http http://localhost")
|
|
|
|
self.assertEqual(None, checker.write())
|
|
|
|
self.assertEqual(2, self.patched['open'].call_count)
|
|
filename = 'check_myservice.cfg'
|
|
expected = [
|
|
('/etc/nagios/nrpe.d/%s' % filename, 'w'),
|
|
('/var/lib/nagios/export/service__a-testunit_%s' % filename, 'w'),
|
|
]
|
|
actual = [x[0] for x in self.patched['open'].call_args_list]
|
|
self.assertEqual(expected, actual)
|
|
outfile = self.patched['open'].return_value.__enter__.return_value
|
|
service_file_contents = """
|
|
#---------------------------------------------------
|
|
# This file is Juju managed
|
|
#---------------------------------------------------
|
|
define service {
|
|
use active-service
|
|
host_name a-testunit
|
|
service_description a-testunit[myservice] Check MyService
|
|
check_command check_nrpe!check_myservice
|
|
servicegroups a
|
|
|
|
}
|
|
"""
|
|
expected = [
|
|
'# check myservice\n',
|
|
'# The following header was added automatically by juju\n',
|
|
'# Modifying it will affect nagios monitoring and alerting\n',
|
|
'# servicegroups: a\n',
|
|
'command[check_myservice]=/usr/lib/nagios/plugins/check_http http://localhost\n',
|
|
service_file_contents,
|
|
]
|
|
actual = [x[0][0] for x in outfile.write.call_args_list]
|
|
self.assertEqual(expected, actual)
|
|
|
|
nrpe_monitors = {'myservice':
|
|
{'command': 'check_myservice',
|
|
}}
|
|
monitors = yaml.dump(
|
|
{"monitors": {"remote": {"nrpe": nrpe_monitors}}})
|
|
relation_set_calls = [
|
|
call(monitors=monitors, relation_id="local-monitors:1"),
|
|
call(monitors=monitors, relation_id="nrpe-external-master:2"),
|
|
]
|
|
self.patched['relation_set'].assert_has_calls(relation_set_calls, any_order=True)
|
|
self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
|
|
exists=4, open=2, listdir=1, relation_get=2,
|
|
relation_ids=3, relation_set=3)
|
|
|
|
def test_max_check_attmpts(self):
|
|
self.patched['config'].return_value = {'nagios_context': 'a',
|
|
'nagios_servicegroups': ''}
|
|
self.patched['exists'].return_value = True
|
|
self.patched['relation_get'].return_value = {
|
|
'egress-subnets': '10.66.111.24/32',
|
|
'ingress-address': '10.66.111.24',
|
|
'private-address': '10.66.111.24'
|
|
}
|
|
|
|
def _rels(rname):
|
|
relations = {
|
|
'local-monitors': 'local-monitors:1',
|
|
'nrpe-external-master': 'nrpe-external-master:2',
|
|
}
|
|
return [relations[rname]]
|
|
self.patched['relation_ids'].side_effect = _rels
|
|
|
|
checker = nrpe.NRPE()
|
|
checker.add_check(shortname="myservice",
|
|
description="Check MyService",
|
|
check_cmd="check_http http://localhost",
|
|
max_check_attempts=8,
|
|
)
|
|
|
|
self.assertEqual(None, checker.write())
|
|
|
|
self.assertEqual(2, self.patched['open'].call_count)
|
|
filename = 'check_myservice.cfg'
|
|
expected = [
|
|
('/etc/nagios/nrpe.d/%s' % filename, 'w'),
|
|
('/var/lib/nagios/export/service__a-testunit_%s' % filename, 'w'),
|
|
]
|
|
actual = [x[0] for x in self.patched['open'].call_args_list]
|
|
self.assertEqual(expected, actual)
|
|
outfile = self.patched['open'].return_value.__enter__.return_value
|
|
service_file_contents = """
|
|
#---------------------------------------------------
|
|
# This file is Juju managed
|
|
#---------------------------------------------------
|
|
define service {
|
|
use active-service
|
|
host_name a-testunit
|
|
service_description a-testunit[myservice] Check MyService
|
|
check_command check_nrpe!check_myservice
|
|
servicegroups a
|
|
max_check_attempts 8
|
|
}
|
|
"""
|
|
expected = [
|
|
'# check myservice\n',
|
|
'# The following header was added automatically by juju\n',
|
|
'# Modifying it will affect nagios monitoring and alerting\n',
|
|
'# servicegroups: a\n',
|
|
'command[check_myservice]=/usr/lib/nagios/plugins/check_http http://localhost\n',
|
|
service_file_contents,
|
|
]
|
|
actual = [x[0][0] for x in outfile.write.call_args_list]
|
|
self.assertEqual(expected, actual)
|
|
|
|
nrpe_monitors = {'myservice':
|
|
{'command': 'check_myservice',
|
|
'max_check_attempts': 8,
|
|
}}
|
|
monitors = yaml.dump(
|
|
{"monitors": {"remote": {"nrpe": nrpe_monitors}}})
|
|
relation_set_calls = [
|
|
call(monitors=monitors, relation_id="local-monitors:1"),
|
|
call(monitors=monitors, relation_id="nrpe-external-master:2"),
|
|
]
|
|
self.patched['relation_set'].assert_has_calls(relation_set_calls, any_order=True)
|
|
self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
|
|
exists=4, open=2, listdir=1, relation_get=2,
|
|
relation_ids=3, relation_set=3)
|
|
|
|
|
|
class NRPECheckTestCase(NRPEBaseTestCase):
|
|
|
|
def test_invalid_shortname(self):
|
|
cases = [
|
|
'invalid:name',
|
|
'',
|
|
]
|
|
for shortname in cases:
|
|
self.assertRaises(nrpe.CheckException, nrpe.Check, shortname,
|
|
'description', '/some/command')
|
|
|
|
def test_valid_shortname(self):
|
|
cases = [
|
|
'1_number_is_fine',
|
|
'dots.are.good',
|
|
'dashes-ok',
|
|
'UPPER_case_allowed',
|
|
'5',
|
|
'@valid',
|
|
]
|
|
for shortname in cases:
|
|
check = nrpe.Check(shortname, 'description', '/some/command')
|
|
self.assertEqual(shortname, check.shortname)
|
|
|
|
def test_write_removes_existing_config(self):
|
|
self.patched['listdir'].return_value = [
|
|
'foo', 'bar.cfg', '_check_shortname.cfg']
|
|
check = nrpe.Check('shortname', 'description', '/some/command')
|
|
|
|
self.assertEqual(None, check.write('testctx', 'hostname', 'testsgrp'))
|
|
|
|
expected = '/var/lib/nagios/export/_check_shortname.cfg'
|
|
self.patched['remove'].assert_called_once_with(expected)
|
|
self.check_call_counts(exists=3, remove=1, open=2, listdir=1)
|
|
|
|
def test_check_write_nrpe_exportdir_not_accessible(self):
|
|
self.patched['exists'].return_value = False
|
|
check = nrpe.Check('shortname', 'description', '/some/command')
|
|
|
|
self.assertEqual(None, check.write('testctx', 'hostname', 'testsgrps'))
|
|
expected = ('Not writing service config as '
|
|
'/var/lib/nagios/export is not accessible')
|
|
self.patched['log'].assert_has_calls(
|
|
[call(expected)], any_order=True)
|
|
self.check_call_counts(log=2, open=1)
|
|
|
|
def test_locate_cmd_no_args(self):
|
|
self.patched['exists'].return_value = True
|
|
|
|
check = nrpe.Check('shortname', 'description', '/bin/ls')
|
|
|
|
self.assertEqual('/bin/ls', check.check_cmd)
|
|
|
|
def test_locate_cmd_not_found(self):
|
|
self.patched['exists'].return_value = False
|
|
check = nrpe.Check('shortname', 'description', 'check_http -x -y -z')
|
|
|
|
self.assertEqual('', check.check_cmd)
|
|
self.assertEqual(2, self.patched['exists'].call_count)
|
|
expected = [
|
|
'/usr/lib/nagios/plugins/check_http',
|
|
'/usr/local/lib/nagios/plugins/check_http',
|
|
]
|
|
actual = [x[0][0] for x in self.patched['exists'].call_args_list]
|
|
self.assertEqual(expected, actual)
|
|
self.check_call_counts(exists=2, log=1)
|
|
expected = 'Check command not found: check_http'
|
|
self.assertEqual(expected, self.patched['log'].call_args[0][0])
|
|
|
|
def test_run(self):
|
|
self.patched['exists'].return_value = True
|
|
command = '/usr/bin/wget foo'
|
|
check = nrpe.Check('shortname', 'description', command)
|
|
|
|
self.assertEqual(None, check.run())
|
|
|
|
self.check_call_counts(exists=1, call=1)
|
|
self.assertEqual(command, self.patched['call'].call_args[0][0])
|
|
|
|
|
|
class NRPEMiscTestCase(NRPEBaseTestCase):
|
|
def test_get_nagios_hostcontext(self):
|
|
rel_info = {
|
|
'nagios_hostname': 'bob-openstack-dashboard-0',
|
|
'private-address': '10.5.3.103',
|
|
'__unit__': u'dashboard-nrpe/1',
|
|
'__relid__': u'nrpe-external-master:2',
|
|
'nagios_host_context': u'bob',
|
|
}
|
|
self.patched['relations_of_type'].return_value = [rel_info]
|
|
self.assertEqual(nrpe.get_nagios_hostcontext(), 'bob')
|
|
|
|
def test_get_nagios_hostname(self):
|
|
rel_info = {
|
|
'nagios_hostname': 'bob-openstack-dashboard-0',
|
|
'private-address': '10.5.3.103',
|
|
'__unit__': u'dashboard-nrpe/1',
|
|
'__relid__': u'nrpe-external-master:2',
|
|
'nagios_host_context': u'bob',
|
|
}
|
|
self.patched['relations_of_type'].return_value = [rel_info]
|
|
self.assertEqual(nrpe.get_nagios_hostname(), 'bob-openstack-dashboard-0')
|
|
|
|
def test_get_nagios_unit_name(self):
|
|
rel_info = {
|
|
'nagios_hostname': 'bob-openstack-dashboard-0',
|
|
'private-address': '10.5.3.103',
|
|
'__unit__': u'dashboard-nrpe/1',
|
|
'__relid__': u'nrpe-external-master:2',
|
|
'nagios_host_context': u'bob',
|
|
}
|
|
self.patched['relations_of_type'].return_value = [rel_info]
|
|
self.assertEqual(nrpe.get_nagios_unit_name(), 'bob:testunit')
|
|
|
|
def test_get_nagios_unit_name_no_hc(self):
|
|
self.patched['relations_of_type'].return_value = []
|
|
self.assertEqual(nrpe.get_nagios_unit_name(), 'testunit')
|
|
|
|
@patch.object(os.path, 'isdir')
|
|
def test_add_init_service_checks(self, mock_isdir):
|
|
def _exists(init_file):
|
|
files = ['/etc/init/apache2.conf',
|
|
'/usr/lib/nagios/plugins/check_upstart_job',
|
|
'/etc/init.d/haproxy',
|
|
'/usr/lib/nagios/plugins/check_status_file.py',
|
|
'/etc/cron.d/nagios-service-check-haproxy',
|
|
'/var/lib/nagios/service-check-haproxy.txt',
|
|
'/usr/lib/nagios/plugins/check_systemd.py'
|
|
]
|
|
return init_file in files
|
|
|
|
self.patched['exists'].side_effect = _exists
|
|
|
|
# Test without systemd and /var/lib/nagios does not exist
|
|
self.patched['init_is_systemd'].return_value = False
|
|
mock_isdir.return_value = False
|
|
bill = nrpe.NRPE()
|
|
services = ['apache2', 'haproxy']
|
|
nrpe.add_init_service_checks(bill, services, 'testunit')
|
|
mock_isdir.assert_called_with('/var/lib/nagios')
|
|
self.patched['call'].assert_not_called()
|
|
expect_cmds = {
|
|
'apache2': '/usr/lib/nagios/plugins/check_upstart_job apache2',
|
|
'haproxy': '/usr/lib/nagios/plugins/check_status_file.py -f '
|
|
'/var/lib/nagios/service-check-haproxy.txt',
|
|
}
|
|
self.assertEqual(bill.checks[0].shortname, 'apache2')
|
|
self.assertEqual(bill.checks[0].check_cmd, expect_cmds['apache2'])
|
|
self.assertEqual(bill.checks[1].shortname, 'haproxy')
|
|
self.assertEqual(bill.checks[1].check_cmd, expect_cmds['haproxy'])
|
|
|
|
# without systemd and /var/lib/nagios does exist
|
|
mock_isdir.return_value = True
|
|
f = MagicMock()
|
|
self.patched['open'].return_value = f
|
|
bill = nrpe.NRPE()
|
|
services = ['apache2', 'haproxy']
|
|
nrpe.add_init_service_checks(bill, services, 'testunit')
|
|
mock_isdir.assert_called_with('/var/lib/nagios')
|
|
self.patched['call'].assert_called_with(
|
|
['/usr/local/lib/nagios/plugins/check_exit_status.pl', '-e', '-s',
|
|
'/etc/init.d/haproxy', 'status'], stdout=f,
|
|
stderr=subprocess.STDOUT)
|
|
|
|
# Test regular services and snap services with systemd
|
|
services = ['apache2', 'haproxy', 'snap.test.test',
|
|
'ceph-radosgw@hostname']
|
|
self.patched['init_is_systemd'].return_value = True
|
|
nrpe.add_init_service_checks(bill, services, 'testunit')
|
|
expect_cmds = {
|
|
'apache2': '/usr/lib/nagios/plugins/check_systemd.py apache2',
|
|
'haproxy': '/usr/lib/nagios/plugins/check_systemd.py haproxy',
|
|
'snap.test.test': '/usr/lib/nagios/plugins/check_systemd.py snap.test.test',
|
|
}
|
|
self.assertEqual(bill.checks[2].shortname, 'apache2')
|
|
self.assertEqual(bill.checks[2].check_cmd, expect_cmds['apache2'])
|
|
self.assertEqual(bill.checks[3].shortname, 'haproxy')
|
|
self.assertEqual(bill.checks[3].check_cmd, expect_cmds['haproxy'])
|
|
self.assertEqual(bill.checks[4].shortname, 'snap.test.test')
|
|
self.assertEqual(bill.checks[4].check_cmd, expect_cmds['snap.test.test'])
|
|
|
|
def test_copy_nrpe_checks(self):
|
|
file_presence = {
|
|
'filea': True,
|
|
'fileb': False}
|
|
self.patched['exists'].return_value = True
|
|
self.patched['glob'].return_value = ['filea', 'fileb']
|
|
self.patched['isdir'].side_effect = [False, True]
|
|
self.patched['isfile'].side_effect = lambda x: file_presence[x]
|
|
nrpe.copy_nrpe_checks()
|
|
self.patched['glob'].assert_called_once_with(
|
|
('/usr/lib/test_charm_dir/hooks/charmhelpers/contrib/openstack/'
|
|
'files/check_*'))
|
|
self.patched['copy2'].assert_called_once_with(
|
|
'filea',
|
|
'/usr/local/lib/nagios/plugins/filea')
|
|
|
|
def test_copy_nrpe_checks_other_root(self):
|
|
file_presence = {
|
|
'filea': True,
|
|
'fileb': False}
|
|
self.patched['exists'].return_value = True
|
|
self.patched['glob'].return_value = ['filea', 'fileb']
|
|
self.patched['isdir'].side_effect = [True, False]
|
|
self.patched['isfile'].side_effect = lambda x: file_presence[x]
|
|
nrpe.copy_nrpe_checks()
|
|
self.patched['glob'].assert_called_once_with(
|
|
('/usr/lib/test_charm_dir/charmhelpers/contrib/openstack/'
|
|
'files/check_*'))
|
|
self.patched['copy2'].assert_called_once_with(
|
|
'filea',
|
|
'/usr/local/lib/nagios/plugins/filea')
|
|
|
|
def test_copy_nrpe_checks_nrpe_files_dir(self):
|
|
file_presence = {
|
|
'filea': True,
|
|
'fileb': False}
|
|
self.patched['exists'].return_value = True
|
|
self.patched['glob'].return_value = ['filea', 'fileb']
|
|
self.patched['isfile'].side_effect = lambda x: file_presence[x]
|
|
nrpe.copy_nrpe_checks(nrpe_files_dir='/other/dir')
|
|
self.patched['glob'].assert_called_once_with(
|
|
'/other/dir/check_*')
|
|
self.patched['copy2'].assert_called_once_with(
|
|
'filea',
|
|
'/usr/local/lib/nagios/plugins/filea')
|