diff --git a/hooks/rabbit_utils.py b/hooks/rabbit_utils.py index d66ffb4a..3f485360 100644 --- a/hooks/rabbit_utils.py +++ b/hooks/rabbit_utils.py @@ -22,6 +22,7 @@ import tempfile import time import shutil import socket +import yaml from collections import OrderedDict @@ -29,8 +30,10 @@ from rabbitmq_context import ( RabbitMQSSLContext, RabbitMQClusterContext, RabbitMQEnvContext, + SSL_CA_FILE, ) +from charmhelpers.contrib.charmsupport import nrpe from charmhelpers.core.templating import render from charmhelpers.contrib.openstack.utils import ( @@ -63,6 +66,7 @@ from charmhelpers.core.hookenv import ( is_leader, leader_get, local_unit, + charm_dir ) from charmhelpers.core.host import ( @@ -72,6 +76,7 @@ from charmhelpers.core.host import ( cmp_pkgrevno, path_hash, service as system_service, + rsync, ) from charmhelpers.contrib.peerstorage import ( @@ -100,6 +105,11 @@ ENABLED_PLUGINS = '/etc/rabbitmq/enabled_plugins' RABBIT_USER = 'rabbitmq' LIB_PATH = '/var/lib/rabbitmq/' HOSTS_FILE = '/etc/hosts' +NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' +SCRIPTS_DIR = '/usr/local/bin' +STATS_CRONFILE = '/etc/cron.d/rabbitmq-stats' +CRONJOB_CMD = ("{schedule} root timeout -k 10s -s SIGINT {timeout} " + "{command} 2>&1 | logger -p local0.notice\n") _named_passwd = '/var/lib/charm/{}/{}.passwd' _local_named_passwd = '/var/lib/charm/{}/{}.local_passwd' @@ -1200,3 +1210,236 @@ def install_or_upgrade_packages(): status_set('maintenance', 'Installing/upgrading RabbitMQ packages') apt_update(fatal=True) apt_install(PACKAGES, fatal=True) + + +def remove_file(path): + """Delete the file or skip it if not exist. + + :param path: the file to delete + :type path: str + """ + if os.path.isfile(path): + os.remove(path) + elif os.path.exists(path): + log('{} path is not file'.format(path), level='ERROR') + else: + log('{} file does not exist'.format(path), level='DEBUG') + + +def sync_nrpe_files(): + """Sync all NRPE-related files. + + Copy all the custom NRPE scripts and create the cron file to run + rabbitmq stats collection + """ + if not os.path.exists(NAGIOS_PLUGINS): + os.makedirs(NAGIOS_PLUGINS, exist_ok=True) + + if config('ssl'): + rsync(os.path.join(charm_dir(), 'files', 'check_rabbitmq.py'), + os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq.py')) + if config('queue_thresholds') and config('stats_cron_schedule'): + rsync(os.path.join(charm_dir(), 'files', 'check_rabbitmq_queues.py'), + os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq_queues.py')) + if config('management_plugin'): + rsync(os.path.join(charm_dir(), 'files', 'check_rabbitmq_cluster.py'), + os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq_cluster.py')) + + if config('stats_cron_schedule'): + rsync(os.path.join(charm_dir(), 'files', 'collect_rabbitmq_stats.sh'), + os.path.join(SCRIPTS_DIR, 'collect_rabbitmq_stats.sh')) + cronjob = CRONJOB_CMD.format( + schedule=config('stats_cron_schedule'), + timeout=config('cron-timeout'), + command=os.path.join(SCRIPTS_DIR, 'collect_rabbitmq_stats.sh')) + write_file(STATS_CRONFILE, cronjob) + + +def remove_nrpe_files(): + """Remove the cron file and all the custom NRPE scripts.""" + if not config('stats_cron_schedule'): + # These scripts are redundant if the value `stats_cron_schedule` + # isn't in the config + remove_file(STATS_CRONFILE) + remove_file(os.path.join(SCRIPTS_DIR, 'collect_rabbitmq_stats.sh')) + + if not config('ssl'): + # This script is redundant if the value `ssl` isn't in the config + remove_file(os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq.py')) + + if not config('queue_thresholds') or not config('stats_cron_schedule'): + # This script is redundant if the value `queue_thresholds` or + # `stats_cron_schedule` isn't in the config + remove_file(os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq_queues.py')) + + if not config('management_plugin'): + # This script is redundant if the value `management_plugin` isn't + # in the config + remove_file(os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq_cluster.py')) + + +def get_nrpe_credentials(): + """Get the NRPE hostname, unit and user details. + + :returns: (hostname, unit, vhosts, user, password) + :rtype: Tuple[str, str, List[Dict[str, str]], str, str] + """ + # Find out if nrpe set nagios_hostname + hostname = nrpe.get_nagios_hostname() + unit = nrpe.get_nagios_unit_name() + + # create unique user and vhost for each unit + current_unit = local_unit().replace('/', '-') + user = 'nagios-{}'.format(current_unit) + vhosts = [{'vhost': user, 'shortname': RABBIT_USER}] + password = get_rabbit_password(user, local=True) + create_user(user, password, ['monitoring']) + + if config('check-vhosts'): + for other_vhost in config('check-vhosts').split(' '): + if other_vhost: + item = {'vhost': other_vhost, + 'shortname': 'rabbit_{}'.format(other_vhost)} + vhosts.append(item) + + return hostname, unit, vhosts, user, password + + +def nrpe_update_vhost_check(nrpe_compat, unit, user, password, vhost): + """Add/Remove the RabbitMQ non-SSL check + + If the SSL is set to `off` or `on`, it will add the non-SSL RabbitMQ check, + otherwise it will remove it. + + :param nrpe_compat: the NRPE class object + :type: nrpe.NRPE + :param unit: NRPE unit + :type: str + :param user: username of NRPE user + :type: str + :param password: password of NRPE user + :type: str + :param vhost: dictionary with vhost and shortname + :type: Dict[str, str] + """ + ssl_config = config('ssl') or '' + if ssl_config.lower() in ['off', 'on']: + log('Adding rabbitmq non-SSL check for {}'.format(vhost['vhost']), + level=DEBUG) + nrpe_compat.add_check( + shortname=vhost['shortname'], + description='Check RabbitMQ {} {}'.format(unit, vhost['vhost']), + check_cmd='{}/check_rabbitmq.py --user {} --password {} ' + '--vhost {}'.format( + NAGIOS_PLUGINS, user, password, vhost['vhost'])) + else: + log('Removing rabbitmq non-SSL check for {}'.format(vhost['vhost']), + level=DEBUG) + nrpe_compat.remove_check( + shortname=vhost['shortname'], + description='Remove check RabbitMQ {} {}'.format( + unit, vhost['vhost']), + check_cmd='{}/check_rabbitmq.py'.format(NAGIOS_PLUGINS)) + + +def nrpe_update_vhost_ssl_check(nrpe_compat, unit, user, password, vhost): + """Add/Remove the RabbitMQ SSL check + + If the SSL is set to `only` or `on`, it will add the SSL RabbitMQ check, + otherwise it will remove it. + + :param nrpe_compat: the NRPE class object + :type: nrpe.NRPE + :param unit: NRPE unit + :type: str + :param user: username of NRPE user + :type: str + :param password: password of NRPE user + :type: str + :param vhost: dictionary with vhost and shortname + :type: Dict[str, str] + """ + ssl_config = config('ssl') or '' + if ssl_config.lower() in ['only', 'on']: + log('Adding rabbitmq SSL check for {}'.format(vhost['vhost']), + level=DEBUG) + nrpe_compat.add_check( + shortname=vhost['shortname'] + "_ssl", + description='Check RabbitMQ (SSL) {} {}'.format( + unit, vhost['vhost']), + check_cmd='{}/check_rabbitmq.py --user {} --password {} ' + '--vhost {} --ssl --ssl-ca {} --port {}'.format( + NAGIOS_PLUGINS, user, password, vhost['vhost'], + SSL_CA_FILE, int(config('ssl_port')))) + else: + log('Removing rabbitmq SSL check for {}'.format(vhost['vhost']), + level=DEBUG) + nrpe_compat.remove_check( + shortname=vhost['shortname'] + "_ssl", + description='Remove check RabbitMQ (SSL) {} {}'.format( + unit, vhost['vhost']), + check_cmd='{}/check_rabbitmq.py'.format(NAGIOS_PLUGINS)) + + +def nrpe_update_queues_check(nrpe_compat, rabbit_dir): + """Add/Remove the RabbitMQ queues check + + The RabbitMQ Queues check should be added if the `queue_thresholds` and + the `stats_cron_schedule` variables are in the configuration. Otherwise, + this check should be removed. + The cron job configured with the `stats_cron_schedule` + variable is responsible for creating the data files read by this check. + + :param nrpe_compat: the NRPE class object + :type: nrpe.NRPE + :param rabbit_dir: path to the RabbitMQ directory + :type: str + """ + stats_datafile = os.path.join( + rabbit_dir, 'data', '{}_queue_stats.dat'.format(get_unit_hostname())) + + if config('queue_thresholds') and config('stats_cron_schedule'): + cmd = "" + # If value of queue_thresholds is incorrect we want the hook to fail + for item in yaml.safe_load(config('queue_thresholds')): + cmd += ' -c "{}" "{}" {} {}'.format(*item) + nrpe_compat.add_check( + shortname=RABBIT_USER + '_queue', + description='Check RabbitMQ Queues', + check_cmd='{}/check_rabbitmq_queues.py{} {}'.format( + NAGIOS_PLUGINS, cmd, stats_datafile)) + else: + log('Removing rabbitmq Queues check', level=DEBUG) + nrpe_compat.remove_check( + shortname=RABBIT_USER + '_queue', + description='Remove check RabbitMQ Queues', + check_cmd='{}/check_rabbitmq_queues.py'.format(NAGIOS_PLUGINS)) + + +def nrpe_update_cluster_check(nrpe_compat, user, password): + """Add/Remove the RabbitMQ cluster check + + If the management_plugin is set to `True`, it will add the cluster RabbitMQ + check, otherwise it will remove it. + + :param nrpe_compat: the NRPE class object + :type: nrpe.NRPE + :param user: username of NRPE user + :type: str + :param password: password of NRPE user + :type: str + """ + if config('management_plugin'): + cmd = '{}/check_rabbitmq_cluster.py --port {} ' \ + '--user {} --password {}'.format( + NAGIOS_PLUGINS, get_managment_port(), user, password) + nrpe_compat.add_check( + shortname=RABBIT_USER + '_cluster', + description='Check RabbitMQ Cluster', + check_cmd=cmd) + else: + log('Removing rabbitmq Cluster check', level=DEBUG) + nrpe_compat.remove_check( + shortname=RABBIT_USER + '_cluster', + description='Remove check RabbitMQ Cluster', + check_cmd='{}/check_rabbitmq_cluster.py'.format(NAGIOS_PLUGINS)) diff --git a/hooks/rabbitmq_server_relations.py b/hooks/rabbitmq_server_relations.py index ed0d5c47..ace3c73a 100755 --- a/hooks/rabbitmq_server_relations.py +++ b/hooks/rabbitmq_server_relations.py @@ -33,38 +33,18 @@ def _add_path(path): _add_path(_root) -try: - import yaml # flake8: noqa -except ImportError: - if sys.version_info.major == 2: - subprocess.check_call(['apt-get', 'install', '-y', 'python-yaml']) - else: - subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml']) - import yaml # flake8: noqa - -try: - import requests # flake8: noqa -except ImportError: - if sys.version_info.major == 2: - subprocess.check_call(['apt-get', 'install', '-y', - 'python-requests']) - else: - subprocess.check_call(['apt-get', 'install', '-y', - 'python3-requests']) - import requests # noqa: F401 - import rabbit_net_utils import rabbit_utils as rabbit import ssl_utils -from rabbitmq_context import SSL_CA_FILE from lib.utils import ( chown, chmod, is_newer, ) +from charmhelpers.contrib.charmsupport import nrpe from charmhelpers.contrib.hahelpers.cluster import ( is_clustered, - is_elected_leader + is_elected_leader, ) from charmhelpers.contrib.openstack.utils import ( is_unit_paused_set, @@ -108,18 +88,14 @@ from charmhelpers.core.hookenv import ( Hooks, UnregisteredHookError, is_leader, - charm_dir, status_set, unit_private_ip, ) from charmhelpers.core.host import ( cmp_pkgrevno, - rsync, service_stop, service_restart, - write_file, ) -from charmhelpers.contrib.charmsupport import nrpe from charmhelpers.contrib.peerstorage import ( peer_echo, @@ -142,14 +118,6 @@ POOL_NAME = SERVICE_NAME RABBIT_DIR = '/var/lib/rabbitmq' RABBIT_USER = 'rabbitmq' RABBIT_GROUP = 'rabbitmq' -NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' -SCRIPTS_DIR = '/usr/local/bin' -STATS_CRONFILE = '/etc/cron.d/rabbitmq-stats' -STATS_DATAFILE = os.path.join(RABBIT_DIR, 'data', - '{}_queue_stats.dat' - ''.format(rabbit.get_unit_hostname())) -CRONJOB_CMD = ("{schedule} root timeout -k 10s -s SIGINT {timeout} " - "{command} 2>&1 | logger -p local0.notice\n") INITIAL_CLIENT_UPDATE_KEY = 'initial_client_update_done' @@ -647,103 +615,38 @@ def ceph_changed(): @hooks.hook('nrpe-external-master-relation-changed') def update_nrpe_checks(): - if os.path.isdir(NAGIOS_PLUGINS): - rsync(os.path.join(charm_dir(), 'files', - 'check_rabbitmq.py'), - os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq.py')) - rsync(os.path.join(charm_dir(), 'files', - 'check_rabbitmq_queues.py'), - os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq_queues.py')) - if config('management_plugin'): - rsync(os.path.join(charm_dir(), 'files', - 'check_rabbitmq_cluster.py'), - os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq_cluster.py')) - - if config('stats_cron_schedule'): - script = os.path.join(SCRIPTS_DIR, 'collect_rabbitmq_stats.sh') - cronjob = CRONJOB_CMD.format(schedule=config('stats_cron_schedule'), - timeout=config('cron-timeout'), - command=script) - rsync(os.path.join(charm_dir(), 'files', - 'collect_rabbitmq_stats.sh'), script) - write_file(STATS_CRONFILE, cronjob) - elif os.path.isfile(STATS_CRONFILE): - os.remove(STATS_CRONFILE) - - # Find out if nrpe set nagios_hostname - hostname = nrpe.get_nagios_hostname() - myunit = nrpe.get_nagios_unit_name() - - # create unique user and vhost for each unit - current_unit = local_unit().replace('/', '-') - user = 'nagios-{}'.format(current_unit) - vhosts = [{'vhost': user, 'shortname': rabbit.RABBIT_USER}] - password = rabbit.get_rabbit_password(user, local=True) + # NOTE (rgildein): This function has been changed to remove redundant + # checks and scripts based on rabbitmq configuration, but the main logic + # was unchanged. + # + # The function logic is based on these three functions: + # 1) copy all the custom NRPE scripts and create cron file + # 2) add NRPE checks and remove redundant + # 2.a) update the NRPE vhost check for TLS and non-TLS + # 2.b) update the NRPE queues check + # 2.c) update the NRPE cluster check + # 3) remove redundant scripts - this must be done after removing + # the relevant check + rabbit.sync_nrpe_files() + hostname, unit, vhosts, user, password = rabbit.get_nrpe_credentials() nrpe_compat = nrpe.NRPE(hostname=hostname) - rabbit.create_user(user, password, ['monitoring']) - - if config('check-vhosts'): - for other_vhost in config('check-vhosts').split(' '): - if other_vhost: - item = {'vhost': other_vhost, - 'shortname': 'rabbit_{}'.format(other_vhost)} - vhosts.append(item) for vhost in vhosts: rabbit.create_vhost(vhost['vhost']) rabbit.grant_permissions(user, vhost['vhost']) - if config('ssl') in ['off', 'on']: - cmd = ('{}/check_rabbitmq.py --user {} --password {} ' - '--vhost {}'.format(NAGIOS_PLUGINS, user, - password, vhost['vhost'])) - log('Adding rabbitmq non-SSL check for {}'.format(vhost['vhost']), - level=DEBUG) - description = 'Check RabbitMQ {} {}'.format(myunit, vhost['vhost']) - nrpe_compat.add_check( - shortname=vhost['shortname'], - description=description, - check_cmd=cmd) - if config('ssl') in ['only', 'on']: - cmd = ('{}/check_rabbitmq.py --user {} --password {} ' - '--vhost {} --ssl --ssl-ca {} --port {}'.format( - NAGIOS_PLUGINS, user, password, vhost['vhost'], - SSL_CA_FILE, int(config('ssl_port')))) - log('Adding rabbitmq SSL check for {}'.format(vhost['vhost']), - level=DEBUG) - description = ('Check RabbitMQ (SSL) {} {}' - .format(myunit, vhost['vhost'])) - nrpe_compat.add_check( - shortname=vhost['shortname'] + "_ssl", - description=description, - check_cmd=cmd) - - if config('queue_thresholds') and config('stats_cron_schedule'): - # Only add queue check if there's also a cronjob for creating stats - cmd = "" - # If value of queue_thresholds is incorrect we want the hook to fail - for item in yaml.safe_load(config('queue_thresholds')): - cmd += ' -c "{}" "{}" {} {}'.format(*item) - nrpe_compat.add_check( - shortname=rabbit.RABBIT_USER + '_queue', - description='Check RabbitMQ Queues', - check_cmd='{}/check_rabbitmq_queues.py{} {}'.format( - NAGIOS_PLUGINS, cmd, STATS_DATAFILE) - ) - if config('management_plugin'): - # add NRPE check - _check_cmd = ( - '{}/check_rabbitmq_cluster.py --port {} --user {} --password {}' - .format(NAGIOS_PLUGINS, rabbit.get_managment_port(), - user, password)) - nrpe_compat.add_check( - shortname=rabbit.RABBIT_USER + '_cluster', - description='Check RabbitMQ Cluster', - check_cmd=_check_cmd) + rabbit.nrpe_update_vhost_check( + nrpe_compat, unit, user, password, vhost) + rabbit.nrpe_update_vhost_ssl_check( + nrpe_compat, unit, user, password, vhost) + rabbit.nrpe_update_queues_check(nrpe_compat, RABBIT_DIR) + rabbit.nrpe_update_cluster_check(nrpe_compat, user, password) nrpe_compat.write() + rabbit.remove_nrpe_files() + @hooks.hook('upgrade-charm') @harden() diff --git a/unit_tests/test_rabbit_utils.py b/unit_tests/test_rabbit_utils.py index bcd8b4fe..c572729c 100644 --- a/unit_tests/test_rabbit_utils.py +++ b/unit_tests/test_rabbit_utils.py @@ -152,6 +152,14 @@ class UtilsTests(CharmTestCase): def setUp(self): super(UtilsTests, self).setUp(rabbit_utils, TO_PATCH) + self.tmp_dir = tempfile.mkdtemp() + self.nrpe_compat = mock.MagicMock() + self.nrpe_compat.add_check = mock.MagicMock() + self.nrpe_compat.remove_check = mock.MagicMock() + + def tearDown(self): + super(UtilsTests, self).tearDown() + self.nrpe_compat.reset_mock() @mock.patch("rabbit_utils.log") def test_update_empty_hosts_file(self, mock_log): @@ -948,3 +956,264 @@ class UtilsTests(CharmTestCase): "active", "message is ignored") self.assertEqual(_expected, rabbit_utils.assess_cluster_status()) + + @mock.patch("rabbit_utils.log") + def test_remove_file(self, mock_log): + """test delete existing and non-existent file""" + with tempfile.NamedTemporaryFile(delete=False) as tmpfile: + rabbit_utils.remove_file(tmpfile.name) + mock_log.asset_not_called() + + mock_log.reset_mock() + rabbit_utils.remove_file(tmpfile.name) + mock_log.assert_has_calls([ + mock.call('{} file does not exist'.format(tmpfile.name), + level='DEBUG') + ]) + + @mock.patch('os.path.isdir') + @mock.patch('rabbit_utils.charm_dir') + @mock.patch('rabbit_utils.config') + @mock.patch('rabbit_utils.rsync') + @mock.patch("rabbit_utils.write_file") + def test_sync_nrpe_files(self, + mock_write_file, + mock_rsync, + mock_config, + mock_charm_dir, + mock_isdir): + """Testing copy the NRPE files""" + self.test_config.set('stats_cron_schedule', '*/1 * * * *') + self.test_config.set('cron-timeout', '300') + self.test_config.unset('management_plugin') + rabbit_utils.NAGIOS_PLUGINS = self.tmp_dir + rabbit_utils.SCRIPTS_DIR = self.tmp_dir + rabbit_utils.STATS_CRONFILE = os.path.join(self.tmp_dir, 'cronfile') + rabbit_utils.CRONJOB_CMD = '{schedule} {timeout} {command}' + + mock_config.side_effect = self.test_config + mock_isdir.return_value = True + mock_charm_dir.side_effect = lambda: self.tmp_dir + rabbit_utils.sync_nrpe_files() + mock_rsync.assert_has_calls([ + mock.call(os.path.join(self.tmp_dir, 'files', 'check_rabbitmq.py'), + os.path.join(self.tmp_dir, 'check_rabbitmq.py')), + mock.call(os.path.join(self.tmp_dir, 'files', + 'check_rabbitmq_queues.py'), + os.path.join(self.tmp_dir, 'check_rabbitmq_queues.py')), + mock.call(os.path.join(self.tmp_dir, 'files', + 'collect_rabbitmq_stats.sh'), + os.path.join(self.tmp_dir, 'collect_rabbitmq_stats.sh')) + ]) + mock_write_file.assert_has_calls([ + mock.call(os.path.join(self.tmp_dir, 'cronfile'), + '*/1 * * * * 300 {}/collect_rabbitmq_stats.sh'.format( + self.tmp_dir)) + ]) + + @mock.patch('rabbit_utils.config') + @mock.patch('rabbit_utils.remove_file') + def test_remove_nrpe_files(self, mock_remove_file, mock_config): + """Testing remove the NRPE scripts and the cron file""" + self.test_config.unset('stats_cron_schedule') + self.test_config.unset('management_plugin') + rabbit_utils.NAGIOS_PLUGINS = self.tmp_dir + rabbit_utils.SCRIPTS_DIR = self.tmp_dir + rabbit_utils.STATS_CRONFILE = os.path.join(self.tmp_dir, 'cronfile') + + mock_config.side_effect = self.test_config + rabbit_utils.remove_nrpe_files() + mock_remove_file.assert_has_calls([ + mock.call(os.path.join(self.tmp_dir, 'cronfile')), + mock.call(os.path.join(self.tmp_dir, 'collect_rabbitmq_stats.sh')), + mock.call(os.path.join(self.tmp_dir, 'check_rabbitmq_queues.py')), + mock.call(os.path.join(self.tmp_dir, 'check_rabbitmq_cluster.py')), + ]) + + @mock.patch('charmhelpers.contrib.charmsupport.nrpe.get_nagios_hostname') + @mock.patch('charmhelpers.contrib.charmsupport.nrpe.get_nagios_unit_name') + @mock.patch('rabbit_utils.create_user') + @mock.patch('rabbit_utils.get_rabbit_password_on_disk') + @mock.patch('rabbit_utils.local_unit') + @mock.patch('rabbit_utils.config') + def test_get_nrpe_credentials(self, + mock_config, + mock_local_unit, + mock_get_rabbit_password_on_disk, + mock_create_user, + mock_get_nagios_unit_name, + mock_get_nagios_hostname): + """Testing get the NRPE credentials""" + self.test_config.unset('check-vhosts') + + mock_get_nagios_hostname.return_value = "foo-0" + mock_get_nagios_unit_name.return_value = "bar-0" + mock_local_unit.return_value = 'unit/0' + mock_config.side_effect = self.test_config + mock_get_rabbit_password_on_disk.return_value = "qwerty" + + nrpe_credentials = rabbit_utils.get_nrpe_credentials() + self.assertTupleEqual(nrpe_credentials, + ('foo-0', + 'bar-0', + [{'vhost': 'nagios-unit-0', + 'shortname': 'rabbitmq'}], + 'nagios-unit-0', + 'qwerty')) + + @mock.patch('charmhelpers.contrib.charmsupport.nrpe.config') + @mock.patch('rabbit_utils.config') + def test_nrpe_update_vhost_check(self, mock_config, mock_config2): + """Testing add and remove RabbitMQ non-SSL check""" + mock_config.side_effect = self.test_config + mock_config2.side_effect = self.test_config + rabbit_utils.NAGIOS_PLUGINS = self.tmp_dir + + # call with ssl set to 'on' + self.test_config.set('ssl', 'on') + rabbit_utils.nrpe_update_vhost_check( + nrpe_compat=self.nrpe_compat, + unit='bar-0', + user='nagios-unit-0', + password='qwerty', + vhost={'vhost': 'nagios-unit-0', 'shortname': 'rabbitmq'}) + self.nrpe_compat.add_check.assert_called_with( + shortname='rabbitmq', + description='Check RabbitMQ bar-0 nagios-unit-0', + check_cmd='{}/check_rabbitmq.py --user nagios-unit-0 ' + '--password qwerty ' + '--vhost nagios-unit-0'.format(self.tmp_dir)) + self.nrpe_compat.remove_check.assert_not_called() + + self.nrpe_compat.reset_mock() + + # call with ssl set to 'only' + self.test_config.set('ssl', 'only') + rabbit_utils.nrpe_update_vhost_check( + nrpe_compat=self.nrpe_compat, + unit='bar-0', + user='nagios-unit-0', + password='qwerty', + vhost={'vhost': 'nagios-unit-0', 'shortname': 'rabbitmq'}) + self.nrpe_compat.add_check.assert_not_called() + self.nrpe_compat.remove_check.assert_called_with( + shortname='rabbitmq', + description='Remove check RabbitMQ bar-0 nagios-unit-0', + check_cmd='{}/check_rabbitmq.py'.format(self.tmp_dir)) + + @mock.patch('charmhelpers.contrib.charmsupport.nrpe.config') + @mock.patch('rabbit_utils.config') + def test_nrpe_update_vhost_ssl_check(self, mock_config, mock_config2): + """Testing add and remove RabbitMQ SSL check""" + mock_config.side_effect = self.test_config + mock_config2.side_effect = self.test_config + rabbit_utils.NAGIOS_PLUGINS = self.tmp_dir + + # call with ssl set to 'on' + self.test_config.set('ssl', 'on') + rabbit_utils.nrpe_update_vhost_ssl_check( + nrpe_compat=self.nrpe_compat, + unit='bar-0', + user='nagios-unit-0', + password='qwerty', + vhost={'vhost': 'nagios-unit-0', 'shortname': 'rabbitmq'}) + self.nrpe_compat.add_check.assert_called_with( + shortname='rabbitmq_ssl', + description='Check RabbitMQ (SSL) bar-0 nagios-unit-0', + check_cmd='{}/check_rabbitmq.py --user nagios-unit-0 ' + '--password qwerty --vhost nagios-unit-0 --ssl ' + '--ssl-ca /etc/rabbitmq/rabbit-server-ca.pem ' + '--port 5671'.format(self.tmp_dir)) + self.nrpe_compat.remove_check.assert_not_called() + + self.nrpe_compat.reset_mock() + + # call with ssl set to 'off' + self.test_config.set('ssl', 'off') + rabbit_utils.nrpe_update_vhost_ssl_check( + nrpe_compat=self.nrpe_compat, + unit='bar-0', + user='nagios-unit-0', + password='qwerty', + vhost={'vhost': 'nagios-unit-0', 'shortname': 'rabbitmq'}) + self.nrpe_compat.add_check.assert_not_called() + self.nrpe_compat.remove_check.assert_called_with( + shortname='rabbitmq_ssl', + description='Remove check RabbitMQ (SSL) bar-0 nagios-unit-0', + check_cmd='{}/check_rabbitmq.py'.format(self.tmp_dir)) + + @mock.patch('charmhelpers.contrib.charmsupport.nrpe.config') + @mock.patch('rabbit_utils.config') + @mock.patch('rabbit_utils.get_unit_hostname') + def test_nrpe_update_queues_check(self, + mock_get_unit_hostname, + mock_config, + mock_config2): + """Testing add and remove RabbitMQ queues check""" + mock_config.side_effect = self.test_config + mock_config2.side_effect = self.test_config + mock_get_unit_hostname.return_value = 'test' + rabbit_utils.NAGIOS_PLUGINS = self.tmp_dir + + # call with stats_cron_schedule set to '*/5 * * * *' + self.test_config.set('stats_cron_schedule', '*/5 * * * *') + rabbit_utils.nrpe_update_queues_check(self.nrpe_compat, self.tmp_dir) + self.nrpe_compat.add_check.assert_called_with( + shortname='rabbitmq_queue', + description='Check RabbitMQ Queues', + check_cmd='{}/check_rabbitmq_queues.py -c "\\*" "\\*" 100 200 ' + '{}/data/test_queue_stats.dat'.format(self.tmp_dir, + self.tmp_dir)) + self.nrpe_compat.remove_check.assert_not_called() + + self.nrpe_compat.reset_mock() + + # call with unset stats_cron_schedule + self.test_config.unset('stats_cron_schedule') + rabbit_utils.nrpe_update_queues_check(self.nrpe_compat, self.tmp_dir) + self.nrpe_compat.add_check.assert_not_called() + self.nrpe_compat.remove_check.assert_called_with( + shortname='rabbitmq_queue', + description='Remove check RabbitMQ Queues', + check_cmd='{}/check_rabbitmq_queues.py'.format(self.tmp_dir)) + + @mock.patch('charmhelpers.contrib.charmsupport.nrpe.config') + @mock.patch('rabbit_utils.config') + @mock.patch('rabbit_utils.get_managment_port') + def test_nrpe_update_cluster_check(self, + mock_get_managment_port, + mock_config, + mock_config2): + """Testing add and remove RabbitMQ cluster check""" + mock_config.side_effect = self.test_config + mock_config2.side_effect = self.test_config + mock_get_managment_port.return_value = '1234' + rabbit_utils.NAGIOS_PLUGINS = self.tmp_dir + + # call with management_plugin set to True + self.test_config.set('management_plugin', True) + rabbit_utils.nrpe_update_cluster_check( + nrpe_compat=self.nrpe_compat, + user='nagios-unit-0', + password='qwerty') + self.nrpe_compat.add_check.assert_called_with( + shortname='rabbitmq_cluster', + description='Check RabbitMQ Cluster', + check_cmd='{}/check_rabbitmq_cluster.py --port 1234 ' + '--user nagios-unit-0 --password qwerty'.format( + self.tmp_dir)) + self.nrpe_compat.remove_check.assert_not_called() + + self.nrpe_compat.reset_mock() + + # call with management_plugin set to False + self.test_config.set('management_plugin', False) + rabbit_utils.nrpe_update_cluster_check( + nrpe_compat=self.nrpe_compat, + user='nagios-unit-0', + password='qwerty') + self.nrpe_compat.add_check.assert_not_called() + self.nrpe_compat.remove_check.assert_called_with( + shortname='rabbitmq_cluster', + description='Remove check RabbitMQ Cluster', + check_cmd='{}/check_rabbitmq_cluster.py'.format(self.tmp_dir)) diff --git a/unit_tests/test_rabbitmq_server_relations.py b/unit_tests/test_rabbitmq_server_relations.py index 404ce216..52a32d03 100644 --- a/unit_tests/test_rabbitmq_server_relations.py +++ b/unit_tests/test_rabbitmq_server_relations.py @@ -307,8 +307,9 @@ class RelationUtil(CharmTestCase): shutil.rmtree(tmpdir) @patch('rabbit_utils.create_user') - @patch('rabbitmq_server_relations.local_unit') - @patch('charmhelpers.contrib.charmsupport.nrpe.NRPE.add_check') + @patch('rabbit_utils.local_unit') + @patch('rabbit_utils.nrpe.NRPE.add_check') + @patch('rabbit_utils.nrpe.NRPE.remove_check') @patch('subprocess.check_call') @patch('rabbit_utils.get_rabbit_password_on_disk') @patch('charmhelpers.contrib.charmsupport.nrpe.relation_ids') @@ -316,10 +317,14 @@ class RelationUtil(CharmTestCase): @patch('charmhelpers.contrib.charmsupport.nrpe.get_nagios_unit_name') @patch('charmhelpers.contrib.charmsupport.nrpe.get_nagios_hostname') @patch('os.fchown') - @patch('rabbitmq_server_relations.charm_dir') + @patch('rabbit_utils.charm_dir') @patch('subprocess.check_output') @patch('rabbitmq_server_relations.config') + @patch('rabbit_utils.config') + @patch('rabbit_utils.remove_file') def test_update_nrpe_checks(self, + mock_remove_file, + mock_config3, mock_config, mock_check_output, mock_charm_dir, mock_fchown, @@ -327,7 +332,8 @@ class RelationUtil(CharmTestCase): mock_get_nagios_unit_name, mock_config2, mock_nrpe_relation_ids, mock_get_rabbit_password_on_disk, - mock_check_call, mock_add_check, + mock_check_call, + mock_remove_check, mock_add_check, mock_local_unit, mock_create_user): @@ -336,8 +342,13 @@ class RelationUtil(CharmTestCase): mock_charm_dir.side_effect = lambda: self.tmp_dir mock_config.side_effect = self.test_config mock_config2.side_effect = self.test_config - rabbitmq_server_relations.STATS_CRONFILE = os.path.join( - self.tmp_dir, "rabbitmq-stats") + mock_config3.side_effect = self.test_config + stats_confile = os.path.join(self.tmp_dir, "rabbitmq-stats") + rabbit_utils.STATS_CRONFILE = stats_confile + nagios_plugins = os.path.join(self.tmp_dir, "nagios_plugins") + rabbit_utils.NAGIOS_PLUGINS = nagios_plugins + scripts_dir = os.path.join(self.tmp_dir, "scripts_dir") + rabbit_utils.SCRIPTS_DIR = scripts_dir mock_get_nagios_hostname.return_value = "foo-0" mock_get_nagios_unit_name.return_value = "bar-0" mock_get_rabbit_password_on_disk.return_value = "qwerty" @@ -348,35 +359,66 @@ class RelationUtil(CharmTestCase): rabbitmq_server_relations.update_nrpe_checks() mock_check_output.assert_any_call( ['/usr/bin/rsync', '-r', '--delete', '--executability', - '%s/files/collect_rabbitmq_stats.sh' % self.tmp_dir, - '/usr/local/bin/collect_rabbitmq_stats.sh'], + '{}/files/collect_rabbitmq_stats.sh'.format(self.tmp_dir), + '{}/collect_rabbitmq_stats.sh'.format(scripts_dir)], stderr=subprocess.STDOUT) # regular check on 5672 - cmd = ('{plugins_dir}/check_rabbitmq.py --user {user} ' - '--password {password} --vhost {vhost}').format( - plugins_dir=rabbitmq_server_relations.NAGIOS_PLUGINS, - user='nagios-unit-0', vhost='nagios-unit-0', - password='qwerty') + cmd_5672 = ('{plugins_dir}/check_rabbitmq.py --user {user} ' + '--password {password} --vhost {vhost}').format( + plugins_dir=nagios_plugins, + user='nagios-unit-0', vhost='nagios-unit-0', + password='qwerty') mock_add_check.assert_any_call( shortname=rabbit_utils.RABBIT_USER, description='Check RabbitMQ {} {}'.format('bar-0', 'nagios-unit-0'), - check_cmd=cmd) + check_cmd=cmd_5672) # check on ssl port 5671 - cmd = ('{plugins_dir}/check_rabbitmq.py --user {user} ' - '--password {password} --vhost {vhost} ' - '--ssl --ssl-ca {ssl_ca} --port {port}').format( - plugins_dir=rabbitmq_server_relations.NAGIOS_PLUGINS, - user='nagios-unit-0', - password='qwerty', - port=int(self.test_config['ssl_port']), - vhost='nagios-unit-0', - ssl_ca=rabbitmq_server_relations.SSL_CA_FILE) + cmd_5671 = ('{plugins_dir}/check_rabbitmq.py --user {user} ' + '--password {password} --vhost {vhost} ' + '--ssl --ssl-ca {ssl_ca} --port {port}').format( + plugins_dir=nagios_plugins, + user='nagios-unit-0', + password='qwerty', + port=int(self.test_config['ssl_port']), + vhost='nagios-unit-0', + ssl_ca=rabbit_utils.SSL_CA_FILE) mock_add_check.assert_any_call( shortname=rabbit_utils.RABBIT_USER + "_ssl", description='Check RabbitMQ (SSL) {} {}'.format('bar-0', 'nagios-unit-0'), - check_cmd=cmd) + check_cmd=cmd_5671) + + # test stats_cron_schedule has been removed + mock_remove_file.reset_mock() + mock_add_check.reset_mock() + mock_remove_check.reset_mock() + self.test_config.unset('stats_cron_schedule') + rabbitmq_server_relations.update_nrpe_checks() + mock_remove_file.assert_has_calls([ + call(stats_confile), + call('{}/collect_rabbitmq_stats.sh'.format(scripts_dir)), + call('{}/check_rabbitmq_queues.py'.format(nagios_plugins)), + call('{}/check_rabbitmq_cluster.py'.format(nagios_plugins))]) + mock_add_check.assert_has_calls([ + call(shortname=rabbit_utils.RABBIT_USER, + description='Check RabbitMQ {} {}'.format('bar-0', + 'nagios-unit-0'), + check_cmd=cmd_5672), + call(shortname=rabbit_utils.RABBIT_USER + "_ssl", + description='Check RabbitMQ (SSL) {} {}'.format( + 'bar-0', 'nagios-unit-0'), + check_cmd=cmd_5671), + ]) + mock_remove_check.assert_has_calls([ + call(shortname=rabbit_utils.RABBIT_USER + '_queue', + description='Remove check RabbitMQ Queues', + check_cmd='{}/check_rabbitmq_queues.py'.format( + nagios_plugins)), + call(shortname=rabbit_utils.RABBIT_USER + '_cluster', + description='Remove check RabbitMQ Cluster', + check_cmd='{}/check_rabbitmq_cluster.py'.format( + nagios_plugins))]) diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py index bccc6dd7..f855cfb9 100644 --- a/unit_tests/test_utils.py +++ b/unit_tests/test_utils.py @@ -115,6 +115,9 @@ class TestConfig(object): raise KeyError self.config[attr] = value + def unset(self, attr): + self.config.pop(attr) + def __getitem__(self, key): return self.get(key)