<commit_before>"""
This file is part of Ludolph: Zabbix API plugin
Copyright (C) 2015-2016 Erigones, s. r. o.

See the LICENSE file for copying permission.
"""
import logging
from datetime import datetime, timedelta

from ludolph_zabbix import __version__
from ludolph.utils import parse_loglevel
from ludolph.web import webhook, request, abort
from ludolph.cron import cronjob
from ludolph.command import CommandError, command
from ludolph.message import IncomingLudolphMessage, red, green
from ludolph.plugins.plugin import LudolphPlugin
from zabbix_api import ZabbixAPI, ZabbixAPIException, ZabbixAPIError

logger = logging.getLogger(__name__)

TIMEOUT = 10


class Zapi(LudolphPlugin):
    """
    Zabbix API connector for LudolphBot.

    Zabbix >= 2.0.6 is required.
    https://www.zabbix.com/documentation/2.0/manual/appendix/api/api
    """
    __version__ = __version__
    _zapi = None

    def __post_init__(self):
        """Log in to zabbix"""
        config = self.config

        # Initialize zapi and try to login
        # HTTP authentication?
        httpuser = config.get('httpuser', None)
        httppasswd = config.get('httppasswd', None)
        # Whether to verify HTTPS server certificate (requires zabbix-api-erigones >= 1.2.2)
        ssl_verify = config.get('ssl_verify', True) not in (False, 'f', 'false', 'False', '0', 0)

        # noinspection PyTypeChecker
        self._zapi = ZabbixAPI(server=config['server'], user=httpuser, passwd=<PASSWORD>passwd, timeout=TIMEOUT,
                               log_level=parse_loglevel(config.get('loglevel', 'INFO')), ssl_verify=ssl_verify)

        # Login and save zabbix credentials
        try:
            logger.info('Zabbix API login')
            self._zapi.login(config['username'], config['password'], save=True)
        except ZabbixAPIException as e:
            logger.critical('Zabbix API login error (%s)', e)

    def zapi(self, method, params=None):
        """
        Acts as a decorator for executing zabbix API commands and checking zabbix API errors.
        """
        # Was never logged in. Repair authentication settings and restart Ludolph.
        if not (self._zapi and self._zapi.logged_in):
            raise CommandError('Zabbix API not available')

        try:
            return self._zapi.call(method, params=params)
        except ZabbixAPIError as ex:  # API command/application problem
            raise CommandError('%(message)s %(code)s: %(data)s' % ex.error)
        except ZabbixAPIException as ex:
            raise CommandError('Zabbix API error (%s)' % ex)  # API connection/transport problem problem

    @webhook('/alert', methods=('POST',))
    def alert(self):
        """
        Process zabbix alert request and send xmpp message to user/room.
        """
        jid = request.forms.get('jid', None)

        if not jid:
            logger.warning('Missing JID in alert request')
            abort(400, 'Missing JID in alert request')

        if jid == self.xmpp.room:
            mtype = 'groupchat'
        else:
            mtype = request.forms.get('mtype', 'normal')

            if mtype not in IncomingLudolphMessage.types:
                logger.warning('Invalid message type (%s) in alert request', mtype)
                abort(400, 'Invalid message type in alert request')

        msg = request.forms.get('msg', '')
        logger.info('Sending monitoring alert to "%s"', jid)
        logger.debug('\twith body: "%s"', msg)
        self.xmpp.msg_send(jid, msg, mtype=mtype)

        return 'Message sent'

    # noinspection PyUnusedLocal
    @command
    def zabbix_version(self, msg):
        """
        Show version of Zabbix API.

        Usage: zabbix-version
        """
        try:
            return 'Zabbix API version: ' + self._zapi.api_version()
        except ZabbixAPIException as ex:
            CommandError('Zabbix API error (%s)' % ex)  # API connection/transport problem problem

    def _get_alerts(self, groupids=None, hostids=None, monitored=True, maintenance=False, skip_dependent=True,
                    expand_description=False, select_hosts=('hostid',), last=None,
                    output=('triggerid', 'state', 'error', 'description', 'priority', 'lastchange'), **kwargs):
        """Return iterator of current zabbix triggers"""
        params = {
            'groupids': groupids,
            'hostids': hostids,
            'monitored': monitored,
            'maintenance': maintenance,
            'skipDependent': skip_dependent,
            'expandDescription': expand_description,
            'filter': {'priority': None},
            'selectHosts': select_hosts,
            'selectLastEvent':  'extend',  # API_OUTPUT_EXTEND
            'output': output,
            'sortfield': 'lastchange',
            'sortorder': 'DESC',  # ZBX_SORT_DOWN
        }

        if last is None:  # Whether to show current or last N alerts
            params['filter']['value'] = 1   # TRIGGER_VALUE_TRUE
        else:
            params['limit'] = last

        if kwargs:
            params.update(**kwargs)

        # If trigger is lost (broken expression) we skip it
        return (trigger for trigger in self.zapi('trigger.get', params) if trigger['hosts'])

    # noinspection PyUnusedLocal
    @command
    def alerts(self, msg, display='all', last=None):
        """
        Show a list of current zabbix alerts with notes and/or trigger items attach to each event ID.

        Usage: alerts [notes|items|all] [last]
        """
        notes = items = False

        if display.startswith('n'):
            notes = True
        elif display.startswith('i'):
            items = True
        elif display.startswith('a'):
            items = notes = True
        elif display.isdigit():
            items = notes = True
            last = display

        _zapi = self._zapi
        out = []
        # Get triggers
        t_output = ('triggerid', 'state', 'error', 'url', 'expression', 'description', 'priority', 'type', 'comments',
                    'lastchange')
        t_hosts = ('hostid', 'name', 'maintenance_status', 'maintenance_type',  'maintenanceid')
        t_options = {'expand_description': True, 'output': t_output, 'select_hosts': t_hosts}

        if items:
            t_options['selectItems'] = ('itemid', 'name')
<commit_msg>Updated alerts command description - issue #4<commit_after>"""
This file is part of Ludolph: Zabbix API plugin
Copyright (C) 2015-2016 Erigones, s. r. o.

See the LICENSE file for copying permission.
"""
import logging
from datetime import datetime, timedelta

from ludolph_zabbix import __version__
from ludolph.utils import parse_loglevel
from ludolph.web import webhook, request, abort
from ludolph.cron import cronjob
from ludolph.command import CommandError, command
from ludolph.message import IncomingLudolphMessage, red, green
from ludolph.plugins.plugin import LudolphPlugin
from zabbix_api import ZabbixAPI, ZabbixAPIException, ZabbixAPIError

logger = logging.getLogger(__name__)

TIMEOUT = 10


class Zapi(LudolphPlugin):
    """
    Zabbix API connector for LudolphBot.

    Zabbix >= 2.0.6 is required.
    https://www.zabbix.com/documentation/2.0/manual/appendix/api/api
    """
    __version__ = __version__
    _zapi = None

    def __post_init__(self):
        """Log in to zabbix"""
        config = self.config

        # Initialize zapi and try to login
        # HTTP authentication?
        httpuser = config.get('httpuser', None)
        httppasswd = config.get('httppasswd', None)
        # Whether to verify HTTPS server certificate (requires zabbix-api-erigones >= 1.2.2)
        ssl_verify = config.get('ssl_verify', True) not in (False, 'f', 'false', 'False', '0', 0)

        # noinspection PyTypeChecker
        self._zapi = ZabbixAPI(server=config['server'], user=httpuser, passwd=<PASSWORD>, timeout=TIMEOUT,
                               log_level=parse_loglevel(config.get('loglevel', 'INFO')), ssl_verify=ssl_verify)

        # Login and save zabbix credentials
        try:
            logger.info('Zabbix API login')
            self._zapi.login(config['username'], config['password'], save=True)
        except ZabbixAPIException as e:
            logger.critical('Zabbix API login error (%s)', e)

    def zapi(self, method, params=None):
        """
        Acts as a decorator for executing zabbix API commands and checking zabbix API errors.
        """
        # Was never logged in. Repair authentication settings and restart Ludolph.
        if not (self._zapi and self._zapi.logged_in):
            raise CommandError('Zabbix API not available')

        try:
            return self._zapi.call(method, params=params)
        except ZabbixAPIError as ex:  # API command/application problem
            raise CommandError('%(message)s %(code)s: %(data)s' % ex.error)
        except ZabbixAPIException as ex:
            raise CommandError('Zabbix API error (%s)' % ex)  # API connection/transport problem problem

    @webhook('/alert', methods=('POST',))
    def alert(self):
        """
        Process zabbix alert request and send xmpp message to user/room.
        """
        jid = request.forms.get('jid', None)

        if not jid:
            logger.warning('Missing JID in alert request')
            abort(400, 'Missing JID in alert request')

        if jid == self.xmpp.room:
            mtype = 'groupchat'
        else:
            mtype = request.forms.get('mtype', 'normal')

            if mtype not in IncomingLudolphMessage.types:
                logger.warning('Invalid message type (%s) in alert request', mtype)
                abort(400, 'Invalid message type in alert request')

        msg = request.forms.get('msg', '')
        logger.info('Sending monitoring alert to "%s"', jid)
        logger.debug('\twith body: "%s"', msg)
        self.xmpp.msg_send(jid, msg, mtype=mtype)

        return 'Message sent'

    # noinspection PyUnusedLocal
    @command
    def zabbix_version(self, msg):
        """
        Show version of Zabbix API.

        Usage: zabbix-version
        """
        try:
            return 'Zabbix API version: ' + self._zapi.api_version()
        except ZabbixAPIException as ex:
            CommandError('Zabbix API error (%s)' % ex)  # API connection/transport problem problem

    def _get_alerts(self, groupids=None, hostids=None, monitored=True, maintenance=False, skip_dependent=True,
                    expand_description=False, select_hosts=('hostid',), last=None,
                    output=('triggerid', 'state', 'error', 'description', 'priority', 'lastchange'), **kwargs):
        """Return iterator of current zabbix triggers"""
        params = {
            'groupids': groupids,
            'hostids': hostids,
            'monitored': monitored,
            'maintenance': maintenance,
            'skipDependent': skip_dependent,
            'expandDescription': expand_description,
            'filter': {'priority': None},
            'selectHosts': select_hosts,
            'selectLastEvent':  'extend',  # API_OUTPUT_EXTEND
            'output': output,
            'sortfield': 'lastchange',
            'sortorder': 'DESC',  # ZBX_SORT_DOWN
        }

        if last is None:  # Whether to show current or last N alerts
            params['filter']['value'] = 1   # TRIGGER_VALUE_TRUE
        else:
            params['limit'] = last

        if kwargs:
            params.update(**kwargs)

        # If trigger is lost (broken expression) we skip it
        return (trigger for trigger in self.zapi('trigger.get', params) if trigger['hosts'])

    # noinspection PyUnusedLocal
    @command
    def alerts(self, msg, display='all', last=None):
        """
        Show a list of current or/and previous (last) zabbix alerts with notes and/or trigger items
        attached to each event ID.

        Usage: alerts [notes|items|all] [last]
        """
        notes = items = False

        if display.startswith('n'):
            notes = True
        elif display.startswith('i'):
            items = True
        elif display.startswith('a'):
            items = notes = True
        elif display.isdigit():
            items = notes = True
            last = display

        _zapi = self._zapi
        out = []
        # Get triggers
        t_output = ('triggerid', 'state', 'error', 'url', 'expression', 'description', 'priority', 'type', 'comments',
                    'lastchange')
        t_hosts = ('hostid', 'name', 'maintenance_status', 'maintenance_type',  'maintenanceid')
        t_options = {'expand_description': True, 'output': t_output, 'select_hosts': t_hosts}

        if items:
            t_options['selectItems'] = ('itemid', 'name')

{'fraction_non_alphanumeric': 0.11102368400346212, 'fraction_numerical': 0.004327641828625384, 'mean_word_length': 2.4444444444444446, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1422169', 'n_tokens_mistral': 3725, 'n_tokens_neox': 3560, 'n_words': 1044}