#!/usr/bin/env python3

"""
Kuhn & Rueß GmbH
Consulting and Development
https://kuhn-ruess.de
"""

from argparse import ArgumentParser
from json import dump, load
from os.path import expanduser, isfile, getctime
from ssl import CERT_REQUIRED, CERT_NONE, SSLContext, PROTOCOL_TLS_CLIENT
from sys import argv
from time import time
from urllib.parse import quote_plus
from xmlrpc.client import ServerProxy


parser = ArgumentParser(
    description="Check Exasol databases"
)

parser.add_argument("-i", "--ipaddress", type=str)
parser.add_argument("-u", "--user", type=str)
parser.add_argument("-p", "--password", type=str)
parser.add_argument("-I", "--ignore", type=str)

args = parser.parse_args()

CONFIG = {
    "hostname" : args.ipaddress,
    "user_name" : quote_plus(args.user),
    "user_password" : quote_plus(args.password),
}

VERIFY = True

if VERIFY:
    SSL_MODE = CERT_REQUIRED
else:
    SSL_MODE = CERT_NONE

CACHE_FILE = expanduser(f"~/tmp/check_mk/exasol_{CONFIG['hostname']}")
CACHE_DURCATION = 3600

def xml_rpc(path):
    """
    XML RPC Helper
    """
    params = CONFIG
    params['path'] = path
    url = 'https://{user_name}:{user_password}@{hostname}/cluster1{path}'.format(**params)
    sslcontext = SSLContext(PROTOCOL_TLS_CLIENT)
    sslcontext.check_hostname = VERIFY
    sslcontext.verify_mode = SSL_MODE
    return ServerProxy(url, context=sslcontext)


CLUSTER = xml_rpc('/')
STORAGE = xml_rpc('/storage')

print("<<<exasol_nodes>>>")
for node in CLUSTER.getNodeList():
    node_info = CLUSTER.getNodeState(node)
    print("{0} {1}".format(node, node_info['status']))

print("<<<exasol_services>>>")
for module, state in CLUSTER.getServiceState():
    print("{0} {1}".format(module, state))

print("<<<exasol_database>>>")
for database_name in CLUSTER.getDatabaseList():
    for db_ignore in args.ignore:
        if db_ignore == database_name:
            continue

    database = xml_rpc('/db_' + quote_plus(database_name))
    db_info = database.getDatabaseInfo()
    print("[[[{0}]]]".format(db_info['name']))

    db_nodes = db_info['nodes']['active']
    db_volume = db_info['persistent volume']
    db_tmp_volume = db_info['temporary volume']

    db_segments = []
    db_volume_info = STORAGE.getVolumeInfo(db_volume)

    for red_layer in range(0, db_volume_info['redundancy']):
        db_segments += db_volume_info['segments'][red_layer]

    if db_tmp_volume == "None":
        db_tmp_segments = ["None"]
    else:
        db_tmp_volume_info = STORAGE.getVolumeInfo(db_tmp_volume)
        db_tmp_segments = db_tmp_volume_info['segments'][0]

    databaseSegmentUsage = (db_info['usage persistent'] / float(len(db_segments)))* \
                                                            db_volume_info['redundancy']
    databaseTempSegmentUsage = (db_info['usage temporary']/ float(len(db_tmp_segments)))

    partion_sizes = {}
    if isfile(CACHE_FILE) and \
        time() - getctime(CACHE_FILE) < CACHE_DURCATION:
        with open(CACHE_FILE, 'r') as f:
            partion_sizes = load(f)
    else:
        for node in sorted(set(db_segments)):
            partitions = xml_rpc('/' + node).getDiskStates()
            for partition in partitions:
                if partition['name'] == db_volume_info['disk']:
                    size = float(partition['size'])
                    partion_sizes[node] = size
            with open(CACHE_FILE, 'w') as f:
                dump(partion_sizes, f, separators=(',', ':'))

    for volume in STORAGE.getVolumeList():
        if volume.startswith('v') and volume not in [db_volume, db_tmp_volume]:
            volumeInfo = STORAGE.getVolumeInfo(volume)
            volumeSizePerNode = (volumeInfo['size'] / float(len(volumeInfo['segments'][0])))

            segments = []
            for red_layer in range(0, volumeInfo['redundancy']):
                segments += volumeInfo['segments'][red_layer]

            for node in segments:
                if node in partion_sizes.keys():
                    partion_sizes[node] -= volumeSizePerNode

        elif volume == db_volume:
            for node in db_segments:
                partion_sizes[node] -= databaseSegmentUsage

        elif volume == db_tmp_volume:
            for node in db_tmp_segments:
                partion_sizes[node] -= databaseTempSegmentUsage


    minPartitionSize = None
    minPartitionNode = ''
    for node in partion_sizes:
        if not minPartitionSize or partion_sizes[node] < minPartitionSize:
            minPartitionSize = partion_sizes[node]
            minPartitionNode = node


    usedSegmentSpace = ((databaseSegmentUsage * db_segments.count(minPartitionNode)) +
                        databaseTempSegmentUsage)

    print("usage "+ str(usedSegmentSpace))
    print("free "+ str(minPartitionSize))
    backup_cache = CACHE_FILE + "_backup"
    backups = []
    if isfile(backup_cache) and \
        time() - getctime(CACHE_FILE) < CACHE_DURCATION:
        with open(backup_cache, 'r') as f:
            backups = load(f)
    else:
        backup_list = database.getBackupList()
        r_found = False
        v_found = False
        for backup in reversed(backup_list):
            backupInfo = database.getBackupInfo(backup[0])
            if backupInfo['usable']:
                volume = backupInfo['volume'][0]
                for backupId in backupInfo['dependencies']:
                    backups.append(database.getBackupInfo((backupId, volume)))
                backups.append(backupInfo)
                break
        with open(backup_cache, 'w') as f:
            dump(backups, f, separators=(',', ':'))


    for backup in backups:
        print("backup_expiration " +  backup['expire date'])

