Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checks for openvswitch and helper function to enable the checks #601

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions charmhelpers/contrib/charmsupport/nrpe.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import yaml

from charmhelpers.core.hookenv import (
DEBUG,
config,
hook_name,
local_unit,
Expand Down Expand Up @@ -509,6 +510,39 @@ def add_haproxy_checks(nrpe, unit_name):
check_cmd='check_haproxy_queue_depth.sh')


def add_openvswitch_checks(nrpe, unit_name):
"""
Add checks for openvswitch

:param NRPE nrpe: NRPE object to add check to
:param str unit_name: Unit name to use in check description
"""
enable_sudo_for_openvswitch_checks()
nrpe.add_check(
shortname='openvswitch',
description='Check Open vSwitch {{{0}}}'.format(unit_name),
check_cmd='check_openvswitch.py')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about make the name more explicit, e.g. check_ovs_interfaces.py or check_ovs_ifaces.py?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was definitely feeling this would be the start of something that could be expanded to more checks as we identify them. The interface errors are just MVP for the current need.



def enable_sudo_for_openvswitch_checks():
sudoers_dir = "/etc/sudoers.d"
sudoers_mode = 0o100440
ovs_sudoers_file = "99-check_openvswitch"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, suggest check_ovs_interfaces; unless the plan is to expand in the future?

ovs_sudoers_entry = ("# Juju owned - do not edit #\n"
"nagios ALL=(root) NOPASSWD: /usr/bin/ovs-vsctl "
"--format=json list Interface\n")
dest = os.path.join(sudoers_dir, ovs_sudoers_file)
try:
with open(dest, "w") as sudoer_file:
sudoer_file.write(ovs_sudoers_entry)
os.chmod(dest, sudoers_mode)
os.chown(dest, uid=0, gid=0)
except (OSError, IOError) as e:
log("Failed to setup sudoers file for check_openvswitch: {}".format(e))
else:
log("Sudoers file for check_openvswitch installed: {}".format(dest), DEBUG)

afreiberger marked this conversation as resolved.
Show resolved Hide resolved

def remove_deprecated_check(nrpe, deprecated_services):
"""
Remove checks fro deprecated services in list
Expand Down
82 changes: 82 additions & 0 deletions charmhelpers/contrib/openstack/files/check_openvswitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Check for known error statuses in OVS.

This script currently checks through the Interface table in OVSDB
for errors. The script is named generically to allow for expanded
status checks in the future.
"""

import argparse
import json
import subprocess
import sys

# This depends on nagios_plugin3 being installed by charm-nrpe
from nagios_plugin3 import CriticalError, UnknownError, try_check


def parse_ovs_interface_errors():
"""Check for errors in OVSDB Interface table."""
try:
cmd = [
"/usr/bin/sudo",
"/usr/bin/ovs-vsctl",
"--format=json",
"list",
"Interface",
]
ovs_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
raise UnknownError(
"UNKNOWN: OVS command '{}' failed. Output: {}".format(
" ".join(cmd), e.output.decode(errors="ignore").rstrip(),
)
)
ovs_interfaces = json.loads(ovs_output.decode(errors="ignore"))
ovs_interface_errors = []
for i in ovs_interfaces["data"]:
# OVSDB internal data is formatted per RFC 7047 5.1
iface = dict(zip(ovs_interfaces["headings"], i))
error = iface["error"]
if isinstance(error, list) and len(error) == 2 and error[0] == "set":
# deserialize the set data into csv string elements
error = ",".join(error[1])
if error:
ovs_interface_errors.append(
"Error on iface {}: {}".format(iface["name"], error)
)

if ovs_interface_errors:
raise CriticalError(
"CRITICAL: Found {} interface error(s) in OVSDB: "
"{}".format(len(ovs_interface_errors), ", ".join(ovs_interface_errors))
)

print(
"OK: No errors found across {} interfaces in openvswitch".format(
len(ovs_interfaces["data"])
)
)


def parse_args(argv=None):
"""Process CLI arguments."""
parser = argparse.ArgumentParser(
prog="check_openvswitch",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As previous

description=(
"this program checks openvswitch interface status and outputs "
"an appropriate Nagios status line"
),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
return parser.parse_args(argv)


def main(argv):
"""Define main subroutine."""
parse_args(argv)
try_check(parse_ovs_interface_errors)


if __name__ == "__main__":
main(sys.argv[1:])
45 changes: 45 additions & 0 deletions tests/contrib/charmsupport/test_nrpe.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,48 @@ def test_copy_nrpe_checks_nrpe_files_dir(self):
self.patched['copy2'].assert_called_once_with(
'filea',
'/usr/local/lib/nagios/plugins/filea')

def test_enable_sudo_for_openvswitch_checks(self):
error_msg = 'test failure'
nrpe.enable_sudo_for_openvswitch_checks()

self.patched['open'].side_effect = IOError(error_msg)
nrpe.enable_sudo_for_openvswitch_checks()
self.patched['open'].side_effect = None

self.patched['chmod'].side_effect = OSError(error_msg)
nrpe.enable_sudo_for_openvswitch_checks()
self.patched['chmod'].side_effect = None

self.patched['chown'].side_effect = OSError(error_msg)
nrpe.enable_sudo_for_openvswitch_checks()
self.patched['chown'].side_effect = None

expected_path = '/etc/sudoers.d/99-check_openvswitch'
expected_success_log = ("Sudoers file for check_openvswitch "
"installed: {}".format(expected_path))
expected_failure_log = ("Failed to setup sudoers file for "
"check_openvswitch: {}".format(error_msg))
self.patched['open'].assert_has_calls(
[call(expected_path, 'w')], any_order=True)
self.patched['chmod'].assert_has_calls(
[call(expected_path, 0o100440)], any_order=True)
self.patched['chown'].assert_has_calls(
[call(expected_path, uid=0, gid=0)], any_order=True)
self.patched['log'].assert_has_calls(
[call(expected_success_log, nrpe.DEBUG),
call(expected_failure_log),
call(expected_failure_log),
call(expected_failure_log)])
self.check_call_counts(log=4, open=4, chmod=3, chown=2)

@patch.object(nrpe, 'enable_sudo_for_openvswitch_checks')
@patch.object(nrpe.NRPE, 'add_check')
def test_add_openvswitch_checks(self, mock_nrpe_add_check, mock_enable_sudo):
foo = nrpe.NRPE()
nrpe.add_openvswitch_checks(foo, 'testunit')
mock_enable_sudo.assert_called_once()
mock_nrpe_add_check.assert_called_once_with(
shortname='openvswitch',
description='Check Open vSwitch {testunit}',
check_cmd='check_openvswitch.py')