#!/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.

# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md

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

import argparse  # pylint: disable=C0413
import base64  # pylint: disable=C0413
import json  # pylint: disable=C0413
import sys  # pylint: disable=C0413

import lib.base  # pylint: disable=C0413
import lib.human  # pylint: disable=C0413
import lib.txt  # pylint: disable=C0413
import lib.url  # pylint: disable=C0413
from lib.globals import STATE_OK, STATE_UNKNOWN  # pylint: disable=C0413

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

DESCRIPTION = """This plugin lets you track the number of active
                users over time, the number of shares in various categories and some storage
                statistics against a Nextcloud server."""

DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_TIMEOUT = 8
DEFAULT_URL = 'http://localhost/nextcloud/ocs/v2.php/apps/serverinfo/api/v1/info'
DEFAULT_USERNAME = 'admin'


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(
        '--insecure',
        help='This option explicitly allows to perform "insecure" SSL connections. '
             'Default: %(default)s',
        dest='INSECURE',
        action='store_true',
        default=DEFAULT_INSECURE,
    )

    parser.add_argument(
        '--no-proxy',
        help='Do not use a proxy. '
             'Default: %(default)s',
        dest='NO_PROXY',
        action='store_true',
        default=DEFAULT_NO_PROXY,
    )

    parser.add_argument(
        '--password',
        help='Nextcloud API password.',
        dest='PASSWORD',
        required = True,
    )

    parser.add_argument(
        '--timeout',
        help='Network timeout in seconds. '
             'Default: %(default)s (seconds)',
        dest='TIMEOUT',
        type=int,
        default=DEFAULT_TIMEOUT,
    )

    parser.add_argument(
        '--url',
        help='Nextcloud API URL. '
             'Default: %(default)s',
        dest='URL',
        default=DEFAULT_URL,
    )

    parser.add_argument(
        '--username',
        help='Nextcloud API username. '
             'Default: %(default)s',
        dest='USERNAME',
        default=DEFAULT_USERNAME,
    )

    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)

    if not args.URL.startswith('http'):
        lib.base.cu('--url parameter has to start with "http://" or https://".')
    if not args.URL.endswith('/info'):
        lib.base.cu('--url parameter has to end in "/info".')
    url = args.URL + '?format=json&skipApps=false'

    # Add the authentication and api request header
    auth = '{}:{}'.format(args.USERNAME, args.PASSWORD)
    encoded_auth = lib.txt.to_text(base64.b64encode(lib.txt.to_bytes(auth)))
    header = {
        'Authorization' : "Basic %s" % encoded_auth,
        'OCS-APIRequest': 'true',
    }
    # and get the info from the API
    jsonst = lib.base.coe(
        lib.url.fetch(
            url,
            header=header,
            insecure=args.INSECURE,
            no_proxy=args.NO_PROXY,
            timeout=args.TIMEOUT,
        )
    )
    try:
        result = json.loads(jsonst)
    except:
        lib.base.cu('ValueError: No JSON object could be decoded')

    if result['ocs']['meta']['status'] != 'ok':
        lib.base.cu('Sorry, something went wrong - no "ok" state returned.')

    # extract some application specific data
    nc_system_apps_num_installed          = result['ocs']['data']['nextcloud']['system']['apps']['num_installed'] # pylint: disable=C0301
    nc_system_apps_num_updates_available  = result['ocs']['data']['nextcloud']['system']['apps']['num_updates_available'] # pylint: disable=C0301
    nc_system_memcache_local              = result['ocs']['data']['nextcloud']['system']['memcache.local'] # pylint: disable=C0301
    nc_system_memcache_locking            = result['ocs']['data']['nextcloud']['system']['memcache.locking'] # pylint: disable=C0301
    nc_system_version                     = result['ocs']['data']['nextcloud']['system']['version']

    nc_storage_num_users                  = result['ocs']['data']['nextcloud']['storage']['num_users'] # pylint: disable=C0301
    nc_storage_num_files                  = result['ocs']['data']['nextcloud']['storage']['num_files'] # pylint: disable=C0301
    nc_storage_num_storages               = result['ocs']['data']['nextcloud']['storage']['num_storages'] # pylint: disable=C0301
    nc_storage_num_storages_local         = result['ocs']['data']['nextcloud']['storage']['num_storages_local'] # pylint: disable=C0301
    nc_storage_num_storages_home          = result['ocs']['data']['nextcloud']['storage']['num_storages_home'] # pylint: disable=C0301
    nc_storage_num_storages_other         = result['ocs']['data']['nextcloud']['storage']['num_storages_other'] # pylint: disable=C0301

    nc_shares_num_fed_shares_received     = result['ocs']['data']['nextcloud']['shares']['num_fed_shares_received'] # pylint: disable=C0301
    nc_shares_num_fed_shares_sent         = result['ocs']['data']['nextcloud']['shares']['num_fed_shares_sent'] # pylint: disable=C0301

    nc_shares_num_shares                  = result['ocs']['data']['nextcloud']['shares']['num_shares'] # pylint: disable=C0301
    nc_shares_num_shares_groups           = result['ocs']['data']['nextcloud']['shares']['num_shares_groups'] # pylint: disable=C0301
    nc_shares_num_shares_link             = result['ocs']['data']['nextcloud']['shares']['num_shares_link'] # pylint: disable=C0301
    nc_shares_num_shares_link_no_password = result['ocs']['data']['nextcloud']['shares']['num_shares_link_no_password'] # pylint: disable=C0301
    nc_shares_num_shares_mail             = result['ocs']['data']['nextcloud']['shares']['num_shares_mail'] if 'num_shares_mail' in result['ocs']['data']['nextcloud']['shares'] else 'n/a' # pylint: disable=C0301
    nc_shares_num_shares_room             = result['ocs']['data']['nextcloud']['shares']['num_shares_room'] if 'num_shares_room' in result['ocs']['data']['nextcloud']['shares'] else 'n/a' # pylint: disable=C0301
    nc_shares_num_shares_user             = result['ocs']['data']['nextcloud']['shares']['num_shares_user'] # pylint: disable=C0301

    nc_server_php_max_execution_time      = result['ocs']['data']['server']['php']['max_execution_time'] # pylint: disable=C0301
    nc_server_php_memory_limit            = result['ocs']['data']['server']['php']['memory_limit']
    nc_server_php_upload_max_filesize     = result['ocs']['data']['server']['php']['upload_max_filesize'] # pylint: disable=C0301
    nc_server_php_version                 = result['ocs']['data']['server']['php']['version']
    nc_server_webserver                   = result['ocs']['data']['server']['webserver']

    nc_server_database_size               = int(result['ocs']['data']['server']['database']['size'])
    nc_server_database_type               = result['ocs']['data']['server']['database']['type']
    nc_server_database_version            = result['ocs']['data']['server']['database']['version']

    nc_active_users_last5min              = result['ocs']['data']['activeUsers']['last5minutes']
    nc_active_users_last1h                = result['ocs']['data']['activeUsers']['last1hour']
    nc_active_users_last24h               = result['ocs']['data']['activeUsers']['last24hours']

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

    # build the (long) multiline message
    msg += '{} users ({}/{}/{} in the last 5min/1h/24h), {} files, {} apps ({} {} available), v{}\n'.format( # pylint: disable=C0301
        nc_storage_num_users,
        nc_active_users_last5min,
        nc_active_users_last1h,
        nc_active_users_last24h,
        lib.human.number2human(nc_storage_num_files),
        nc_system_apps_num_installed,
        nc_system_apps_num_updates_available,
        lib.txt.pluralize('update', nc_system_apps_num_updates_available),
        nc_system_version,
    )
    msg += '* Shares: {} ({} {}, {} {} [{} w/o password], {} {}, {} {}, {} {}, {} federated sent)\n'.format( # pylint: disable=C0301
        nc_shares_num_shares,
        nc_shares_num_shares_groups,
        lib.txt.pluralize('group', nc_shares_num_shares_groups),
        nc_shares_num_shares_link,
        lib.txt.pluralize('link', nc_shares_num_shares_link),
        nc_shares_num_shares_link_no_password,
        nc_shares_num_shares_mail,
        lib.txt.pluralize('mail', nc_shares_num_shares_mail),
        nc_shares_num_shares_room,
        lib.txt.pluralize('room', nc_shares_num_shares_room),
        nc_shares_num_shares_user,
        lib.txt.pluralize('user', nc_shares_num_shares_user),
        nc_shares_num_fed_shares_sent,
    )
    msg += '* Federated Shares: {} received\n'.format(nc_shares_num_fed_shares_received)
    msg += '* Storages: {} ({} home, {} other, {} local)\n'.format(
        nc_storage_num_storages, nc_storage_num_storages_home,
        nc_storage_num_storages_other,
        nc_storage_num_storages_local,
    )
    msg += '* PHP: v{}, upload_max_filesize={}, max_execution_time={}s, memory_limit={}\n'.format(
        nc_server_php_version,
        lib.human.bytes2human(nc_server_php_upload_max_filesize),
        nc_server_php_max_execution_time,
        lib.human.bytes2human(nc_server_php_memory_limit),
    )
    msg += '* DB: {} v{}, size={}\n'.format(
        nc_server_database_type,
        nc_server_database_version,
        lib.human.bytes2human(nc_server_database_size),
    )
    msg += '* Web: {}, local memcache: {}, locking memcache: {}'.format(
        nc_server_webserver,
        nc_system_memcache_local.replace('\\OC\\', ''),
        nc_system_memcache_locking.replace('\\OC\\', ''),
    )

    perfdata += lib.base.get_perfdata('nc_system_apps_num_installed', nc_system_apps_num_installed, None, None, None, 0, None) # pylint: disable=C0301

    perfdata += lib.base.get_perfdata('nc_storage_num_users', nc_storage_num_users, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_storage_num_files', nc_storage_num_files, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_storage_num_storages', nc_storage_num_storages, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_storage_num_storages_local', nc_storage_num_storages_local, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_storage_num_storages_home', nc_storage_num_storages_home, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_storage_num_storages_other', nc_storage_num_storages_other, None, None, None, 0, None) # pylint: disable=C0301

    perfdata += lib.base.get_perfdata('nc_shares_num_fed_shares_received', nc_shares_num_fed_shares_received, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_fed_shares_sent', nc_shares_num_fed_shares_sent, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares', nc_shares_num_shares, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares_groups', nc_shares_num_shares_groups, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares_link', nc_shares_num_shares_link, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares_link_no_password', nc_shares_num_shares_link_no_password, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares_mail', nc_shares_num_shares_mail, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares_room', nc_shares_num_shares_room, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_shares_num_shares_user', nc_shares_num_shares_user, None, None, None, 0, None) # pylint: disable=C0301

    perfdata += lib.base.get_perfdata('nc_server_database_size', nc_server_database_size, 'B', None, None, 0, None) # pylint: disable=C0301

    perfdata += lib.base.get_perfdata('nc_active_users_last5min', nc_active_users_last5min, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_active_users_last1h', nc_active_users_last1h, None, None, None, 0, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('nc_active_users_last24h', nc_active_users_last24h, None, None, None, 0, None) # pylint: disable=C0301

    # over and out
    lib.base.oao(msg, state, perfdata)


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