nagios-charmers team mailing list archive
-
nagios-charmers team
-
Mailing list archive
-
Message #00347
[Merge] ~aieri/hw-health-charm:oo-rewrite-cleanup1 into hw-health-charm:oo-rewrite-integration
Andrea Ieri has proposed merging ~aieri/hw-health-charm:oo-rewrite-cleanup1 into hw-health-charm:oo-rewrite-integration with ~aieri/hw-health-charm:oo-rewrite-core as a prerequisite.
Requested reviews:
Canonical IS Reviewers (canonical-is-reviewers)
For more details, see:
https://code.launchpad.net/~aieri/hw-health-charm/+git/hw-health-charm/+merge/364756
Part 3 of the superseded MR#364694
--
Your team Nagios Charm developers is subscribed to branch hw-health-charm:oo-rewrite-integration.
diff --git a/src/config.yaml b/src/config.yaml
index 37dda77..a6e8797 100644
--- a/src/config.yaml
+++ b/src/config.yaml
@@ -20,3 +20,10 @@ options:
type: boolean
default: True
description: Enable the use of freeipmi tools to monitor hardware status.
+ ipmi_check_options:
+ type: string
+ default: ''
+ description: |
+ Additional options to be passed to check_ipmi_sensor. For non-standard
+ ipmi implementations you might for example need
+ "--seloptions '--assume-system-event-records'"
diff --git a/src/lib/hwhealth/tooling.py b/src/lib/hwhealth/tooling.py
deleted file mode 100644
index 40b140c..0000000
--- a/src/lib/hwhealth/tooling.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# -*- coding: us-ascii -*-
-import glob
-import os
-import shutil
-import subprocess
-import tempfile
-import zipfile
-
-from charmhelpers.contrib.charmsupport import nrpe
-from charmhelpers.core import hookenv
-
-TOOLING = {
- 'megacli': {
- 'snap': 'megacli',
- 'filename': 'megaraid/check_megacli.py',
- 'cronjob': 'megaraid/check_megacli.sh'
- },
- 'sas2ircu': {
- 'snap': 'sas2ircu',
- 'filename': 'sas2ircu/check_sas2ircu.py',
- 'cronjob': 'sas2ircu/check_sas2ircu.sh'
- },
- 'sas3ircu': {
- 'snap': 'sas3ircu',
- 'filename': 'sas3ircu/check_sas3ircu.py',
- 'cronjob': 'sas3ircu/check_sas3ircu.sh'
- },
- 'mdadm': {
- 'filename': 'mdadm/check_mdadm.py',
- 'cronjob': 'mdadm/cron_mdadm.py'
- },
- 'ipmi': {
- 'filename': 'ipmi/check_ipmi_sensor',
- 'sudoers': 'ipmi/check_ipmi_sensor_sudoer'
- },
-}
-
-PLUGINS_DIR = '/usr/local/lib/nagios/plugins/'
-TOOLS_DIR = '/usr/local/bin/'
-CRONJOB_PATH = '/etc/cron.d/hw_health_{}'
-SUDOERS_DIR = '/etc/sudoers.d'
-
-
-def install_resource(tools, resource):
- """Install hardware diagnostic tools from a charm resource
-
- :param tools: set of tools to include
- :param resource: resource object
- :return: boolean, status of installation
- """
- if not isinstance(tools, set):
- try:
- tools = set(tools)
- except TypeError as e:
- hookenv.log("install_resources called with invalid 'tools', {}".format(e))
- return False
- if not tools:
- return False
- elif tools & {'mdadm', 'ipmi'}:
- tools = tools - {'mdadm', 'ipmi'}
- if not tools:
- return True
-
- if not isinstance(resource, str) \
- or not resource.endswith('.zip') \
- or not os.path.exists(resource):
- hookenv.log("The resource 'tools' does not end with .zip or does not exist")
- return False
-
- with tempfile.TemporaryDirectory() as tmpdir:
- try:
- with zipfile.ZipFile(resource, 'r') as zf:
- zf.extractall(tmpdir)
-
- count = 0
- for filepath in glob.glob(os.path.join(tmpdir, '*')):
- filebasename = os.path.basename(filepath)
- if filebasename in tools \
- and filebasename in TOOLING \
- and os.path.isfile(filepath):
- os.chmod(filepath, 0o755)
- shutil.chown(filepath, user=0, group=0)
- shutil.copy2(filepath, TOOLS_DIR)
- count += 1
-
- return len(tools) == count and count > 0
-
- except zipfile.BadZipFile as error:
- hookenv.log('BadZipFile: {}'.format(error), hookenv.ERROR)
-
- except PermissionError as error:
- hookenv.log(
- 'Unable to unzip the resource: {}'.format(error), hookenv.ERROR)
-
- return False
-
-
-def install_tools(tools, method='snap'):
- """Install hardware management tools from a snap
-
- :param tools: list of tools to install
- :param method: default to snap if unspecified, other means have yet to be added
- :return:
- """
- count = 0
- for tool in tools:
- if tool in TOOLING and method in TOOLING[tool]:
- if method == 'snap':
- try:
- # snap.install(TOOLING[tool][method])
- count += 1
- except Exception as error:
- hookenv.log(
- 'Snap install error: {}'.format(error), hookenv.ERROR)
- elif tool == 'mdadm':
- # Note(aluria): mdadm is assumed to be installed by default
- count += 1
-
- return len(tools) >= count > 0
-
-
-def _get_filepath(filename):
- filepath = os.path.join(hookenv.charm_dir(), 'files', filename)
- if os.path.exists(filepath):
- return filepath
-
-
-def configure_tools(tools):
- count = 0
- for tool in tools:
- if tool in TOOLING:
- if 'cronjob' in TOOLING[tool]:
- filepath = _get_filepath(TOOLING[tool]['cronjob'])
- cronjob_scriptname = os.path.basename(TOOLING[tool]['cronjob'])
- if filepath:
- subprocess.call(filepath)
- shutil.copy2(filepath, PLUGINS_DIR)
- hookenv.log(
- 'Cronjob script [{}] copied to {}'.format(
- filepath, PLUGINS_DIR), hookenv.DEBUG)
- else:
- continue
- cronjob_file = CRONJOB_PATH.format(tool)
- cronjob_line = '*/5 * * * * root {}\n'
- cronjob_script = os.path.join(PLUGINS_DIR, cronjob_scriptname)
- with open(cronjob_file, 'w') as fd:
- fd.write(cronjob_line.format(cronjob_script))
- hookenv.log(
- 'Cronjob configured at {}'.format(cronjob_file),
- hookenv.DEBUG)
- count += 1
- hookenv.log('Cronjob for tool [{}] configured'.format(tool))
-
- if 'filename' in TOOLING[tool]:
- filepath = _get_filepath(TOOLING[tool]['filename'])
- if filepath:
- shutil.copy2(filepath, PLUGINS_DIR)
- count += 1
- hookenv.log(
- 'NRPE script for tool [{}] configured'.format(tool))
- if 'sudoers' in TOOLING[tool]:
- sudoers_path = _get_filepath(TOOLING[tool]['sudoers'])
- if sudoers_path:
- shutil.copy2(sudoers_path, SUDOERS_DIR)
- count += 1
- hookenv.log('sudoers entry for tool [{}] added'.format(tool))
- return count > 0
-
-
-def configure_cronjobs(tools):
- pass
-
-
-def configure_nrpe_checks(tools):
- if not configure_tools(tools):
- hookenv.log(
- "No tools configured. NRPE checks can't be configured",
- hookenv.DEBUG)
- return False
-
- count = 0
- hostname = nrpe.get_nagios_hostname()
- nrpe_setup = nrpe.NRPE(hostname=hostname, primary=False)
- for tool in tools:
- if tool in TOOLING and 'filename' in TOOLING[tool]:
- scriptname = os.path.basename(TOOLING[tool]['filename'])
- cmd = os.path.join(PLUGINS_DIR, scriptname) # TODO args to the command?
- if os.path.exists(cmd):
- nrpe_setup.add_check(tool, '{} Hardware Health', cmd)
- nrpe_setup.write()
- count += 1
- else:
- cronjob_file = os.path.join('/etc/cron.d', tool)
- if os.path.exists(cronjob_file):
- os.unlink(cronjob_file)
- hookenv.log(
- 'Cronjob file [{}] removed'.format(cronjob_file),
- hookenv.DEBUG)
-
- return count > 0
-
-
-def remove_nrpe_checks(tools):
- if len(tools) == 0:
- return False
-
- hostname = nrpe.get_nagios_hostname()
- nrpe_setup = nrpe.NRPE(hostname=hostname, primary=False)
- count = 0
- for tool in tools:
- if tool not in TOOLING:
- continue
- nrpe_setup.remove_check(
- shortname=tool, check_cmd='check_{}'.format(tool))
-
- if os.path.exists(CRONJOB_PATH.format(tool)):
- os.remove(CRONJOB_PATH.format(tool))
-
- hookenv.log('Check removed from nrpe: {}'.format(tool))
- count += 1
-
- if count > 0:
- nrpe_setup.write()
- return True
- return False
diff --git a/src/tests/unit/test_tooling.py b/src/tests/unit/test_tooling.py
deleted file mode 100644
index 3421b7f..0000000
--- a/src/tests/unit/test_tooling.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import os # noqa: F401
-import sys
-import unittest
-import unittest.mock as mock
-import zipfile
-
-
-sys.path.append('lib/hwhealth')
-import tooling # noqa: E402
-
-
-class TestTooling(unittest.TestCase):
- def test_install_resource_notools(self):
- self.assertEqual(tooling.install_resource([], False), False)
- self.assertEqual(
- tooling.install_resource([], '/path/to/resource'), False)
-
- def test_install_resource_noresource_nomdadm(self):
- self.assertEqual(tooling.install_resource(['unittest'], False), False)
-
- def test_install_resource_noresource_yesmdadm(self):
- self.assertEqual(tooling.install_resource(['mdadm'], False), True)
-
- @mock.patch('glob.glob')
- @mock.patch('os.chmod')
- @mock.patch('os.path.exists')
- @mock.patch('os.path.isfile')
- @mock.patch('shutil.chown')
- @mock.patch('shutil.copy2')
- @mock.patch('tempfile.TemporaryDirectory')
- @mock.patch('zipfile.ZipFile')
- def test_install_resource_ok(self, zfile, tmpdir, shutil_copy2,
- shutil_chown, path_isfile, path_exists,
- os_chmod, glob_glob):
- class _WrapCls(object):
- def __init__(cls):
- cls.extractall = lambda x: None
- cls.close = lambda: None
-
- class Test_resource_zipfile(object):
- def __enter__(cls):
- return _WrapCls()
-
- def __exit__(cls, type, value, traceback):
- pass
-
- class Test_resource_tmpdir(Test_resource_zipfile):
- def __enter__(cls):
- return ''
-
- path_exists.return_value = True
- zfile.return_value = Test_resource_zipfile()
- tmpdir.return_value = Test_resource_tmpdir()
- glob_glob.return_value = ['/tmp/test/megacli']
- os_chmod.return_value = None
- path_isfile.return_value = True
- shutil_chown.return_value = None
- shutil_copy2.return_value = None
- actual = tooling.install_resource(['megacli'], '/tmp/test.zip')
- expected = True
- self.assertEqual(actual, expected)
-
- @mock.patch('os.path.exists')
- @mock.patch('zipfile.ZipFile')
- def test_install_resource_zipfile_error(self, zfile, path_exists):
- path_exists.return_value = True
- zfile.side_effect = zipfile.BadZipFile('Intended error for unit test')
- actual = tooling.install_resource(['megacli'], '/tmp/test.zip')
- expected = False
- self.assertEqual(actual, expected)
-
- @mock.patch('glob.glob')
- @mock.patch('os.path.exists')
- @mock.patch('os.path.isfile')
- @mock.patch('tempfile.TemporaryDirectory')
- @mock.patch('zipfile.ZipFile')
- def test_install_resource_nomatchedresource(self, zfile, tmpdir,
- path_isfile, path_exists,
- glob_glob):
- class _WrapCls(object):
- def __init__(cls):
- cls.extractall = lambda x: None
- cls.close = lambda: None
-
- class Test_resource_zipfile(object):
- def __enter__(cls):
- return _WrapCls()
-
- def __exit__(cls, type, value, traceback):
- pass
-
- class Test_resource_tmpdir(Test_resource_zipfile):
- def __enter__(cls):
- return ''
-
- path_exists.return_value = True
- zfile.return_value = Test_resource_zipfile()
- tmpdir.return_value = Test_resource_tmpdir()
- glob_glob.return_value = ['/tmp/test/megacli']
- path_isfile.return_value = True
- self.assertEqual(
- tooling.install_resource(['unittest'], '/tmp/test.zip'), False)
-
- def test_install_tools_notools(self):
- self.assertEqual(tooling.install_tools([]), False)
-
- def test_install_tools_unsupported_tools(self):
- actual = tooling.install_tools(['unittest1', 'unittest2'])
- expected = False
- self.assertEqual(actual, expected)
-
- def test_install_tools_supported_tools(self):
- self.assertEqual(tooling.install_tools(['megacli', 'sas2ircu']), True)
-
- def test_install_tools_mdadm(self):
- self.assertEqual(tooling.install_tools(['mdadm']), True)
-
- @mock.patch('os.environ')
- @mock.patch('os.path.exists')
- @mock.patch('os.path.join')
- def test_get_filepath(self, path_join, path_exists, environ):
- expected = 'unittest1'
- environ.return_value = {'CHARM_DIR': 'xxx'}
- path_join.return_value = expected
- path_exists.return_value = True
- self.assertEqual(tooling._get_filepath('asdfasdf'), expected)
-
- def test_configure_tools_notools(self):
- self.assertEqual(tooling.configure_tools([]), False)
-
- def test_configure_tools_unsupported_tools(self):
- self.assertEqual(
- tooling.configure_tools(['unittest1', 'unittest2']), False)
Follow ups