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

dhcp-scope-usage add switch for displaying only scopes in error. #788

Open
drapiti opened this issue Nov 12, 2024 · 2 comments
Open

dhcp-scope-usage add switch for displaying only scopes in error. #788

drapiti opened this issue Nov 12, 2024 · 2 comments
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@drapiti
Copy link

drapiti commented Nov 12, 2024

Describe the solution you'd like

Much like the disk-usage check please introduce a switch to the dhcp-scope plugin which allows to filter output for scopes only in an error state. Some servers have thousands of scopes it is not practical to include all of them in the output as is the case at moment.

Additional context

No response

@drapiti drapiti added the enhancement New feature or request label Nov 12, 2024
@markuslf
Copy link
Member

Sounds good.

@markuslf markuslf self-assigned this Nov 13, 2024
@markuslf markuslf added this to the M006 milestone Nov 13, 2024
@drapiti
Copy link
Author

drapiti commented Dec 6, 2024

Providing fixed code for this functionality and an additional argument to exclude specific scopes from the check:

#!/usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author:  Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
#          https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

"""See the check's README for more details.
"""

import argparse  # pylint: disable=C0413
import re  # pylint: disable=C0413
import sys  # pylint: disable=C0413

import lib.args  # pylint: disable=C0413
import lib.base  # pylint: disable=C0413
import lib.powershell  # pylint: disable=C0413
import lib.test  # pylint: disable=C0413
import lib.winrm  # pylint: disable=C0413
from lib.globals import (STATE_CRIT, STATE_OK,  # pylint: disable=C0413
                          STATE_UNKNOWN, STATE_WARN)

__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2024120601'

DESCRIPTION = """Checks the IPv4 scope usage for a Windows DHCP server service."""

DEFAULT_HOSTNAME = 'localhost'
DEFAULT_WARN = 80
DEFAULT_CRIT = 90


def parse_args():
    """Parse command line arguments using argparse.
    """
    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument(
        '-V', '--version',
        action='version',
        version='%(prog)s: v{} by {}'.format(__version__, __author__)
    )

    parser.add_argument(
        '--always-ok',
        help='Always returns OK.',
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '-c', '--critical',
        help='Set the CRIT threshold as a percentage. Default: >= %(default)s',
        dest='CRIT',
        type=int,
        default=DEFAULT_CRIT,
    )

    parser.add_argument(
        '--exclude-scope',
        help='Comma-separated list of scope IDs to exclude from the check.',
        dest='EXCLUDE_SCOPES',
        type=lib.args.csv,
        default=[],
    )

    parser.add_argument(
        '-H', '--hostname',
        help='Specifies the DNS name, or IPv4 or IPv6 address, of the target computer that runs the DHCP server service. Default: %(default)s',
        dest='HOSTNAME',
        default=DEFAULT_HOSTNAME,
    )

    parser.add_argument(
        '--test',
        help='For unit tests. Needs "path-to-stdout-file,path-to-stderr-file,expected-retc".',
        dest='TEST',
        type=lib.args.csv,
    )

    parser.add_argument(
        '-w', '--warning',
        help='Set the WARN threshold as a percentage. Default: >= %(default)s',
        dest='WARN',
        type=int,
        default=DEFAULT_WARN,
    )

    parser.add_argument(
        '--brief',
        help='Only show scopes in an error state (warning or critical).',
        dest='BRIEF',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--winrm-domain',
        help='WinRM Domain Name. Default: %(default)s',
        dest='WINRM_DOMAIN',
        default=None,
    )

    parser.add_argument(
        '--winrm-hostname',
        help='Target Windows computer on which the Windows commands are to be executed. Default: %(default)s',
        dest='WINRM_HOSTNAME',
        default=None,
    )

    parser.add_argument(
        '--winrm-password',
        help='WinRM Account Password. Default: %(default)s',
        dest='WINRM_PASSWORD',
        default=None,
    )

    parser.add_argument(
        '--winrm-transport',
        help='WinRM transport type. Default: %(default)s',
        dest='WINRM_TRANSPORT',
        choices=['basic', 'ntlm', 'kerberos'],
        default='ntlm',
    )

    parser.add_argument(
        '--winrm-username',
        help='WinRM Account Name. Default: %(default)s',
        dest='WINRM_USERNAME',
        default=None,
    )

    return parser.parse_args()


def main():
    """The main function. Hier spielt die Musik.
    """

    # parse the command line, exit with UNKNOWN if it fails
    try:
        args = parse_args()
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    # fetch data
    if args.TEST is None:
        # https://docs.microsoft.com/en-us/powershell/module/dhcpserver/get-dhcpserverv4scopestatistics
        cmd = 'Get-DhcpServerv4ScopeStatistics -ComputerName "{}"'.format(args.HOSTNAME)
        if args.WINRM_HOSTNAME:
            result = lib.winrm.run_ps(args, cmd)
        else:
            if lib.base.LINUX:
                lib.base.cu('Running this check plugin directly on Linux is not supported. Use the --winrm parameters.')
            result = lib.powershell.run_ps(cmd)
        if not result:
            lib.base.cu('Got nothing back.')
        stdout, stderr, retc = result['stdout'], result['stderr'], result['retc']
    else:
        # do not call the command, put in test data
        stdout, stderr, retc = lib.test.test(args.TEST)

    if retc != 0:
        lib.base.oao(stderr, STATE_WARN)

    # init some vars
    msg = ''
    state = STATE_OK
    perfdata = ''

    # get state and build the message
    data_section = False
    for line in stdout.splitlines():
        # ignore all header lines including "----"
        if not line:
            continue
        if not data_section:
            data_section = line.startswith('----')
            continue

        scope = line.split()
        scope_id = scope[0]

        # Skip excluded scopes
        if scope_id in args.EXCLUDE_SCOPES:
            continue

        # "PercentageInUse" is a floating point number and uses the user's preferred locale
        # number format. we don't care about the fraction part, separated depending on
        # the user's locale by `.`, `,` or whatever - we simply cut it off.
        scope_used = re.split(r'\D+', scope[3])[0]

        scope_state = lib.base.get_state(scope_used, args.WARN, args.CRIT)
        state = lib.base.get_worst(scope_state, state)
        
        # Only add to message if not in brief mode or in an error state
        if not args.BRIEF or scope_state != STATE_OK:
            msg += '* {}: {}% used{}\n'.format(
                scope_id,
                scope_used,
                lib.base.state2str(scope_state, prefix=' '),
            )
            perfdata += lib.base.get_perfdata('scope_{}'.format(scope_id), scope_used, '%', args.WARN, args.CRIT, 0, 100)

    # over and out
    if state == STATE_CRIT:
        msg = 'There are one or more criticals.\n\n' + msg
    elif state == STATE_WARN:
        msg = 'There are one or more warnings.\n\n' + msg
    else:
        msg = 'Everything is ok.\n\n' + msg if not args.BRIEF else 'Everything is ok.'
    
    lib.base.oao(msg, state, perfdata, always_ok=args.ALWAYS_OK)


if __name__ == '__main__':
    try:
        main()
    except Exception:   # pylint: disable=W0703
        lib.base.cu()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants