#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# ails.  You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

def esx_vsphere_vm_convert(info):
    data = {}
    for line in info:
        data[line[0]] = line[1:]
    return data

#   .--Memory--------------------------------------------------------------.
#   |               __  __                                                 |
#   |              |  \/  | ___ _ __ ___   ___  _ __ _   _                 |
#   |              | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |                |
#   |              | |  | |  __/ | | | | | (_) | |  | |_| |                |
#   |              |_|  |_|\___|_| |_| |_|\___/|_|   \__, |                |
#   |                                                |___/                 |
#   '----------------------------------------------------------------------'


def inventory_esx_vsphere_vm_mem(info):
    data = esx_vsphere_vm_convert(info).keys()
    if 'summary.quickStats.guestMemoryUsage' in data:
        return [(None, {})]

def check_esx_vsphere_vm_mem(_no_item, _no_params, info):
    data = esx_vsphere_vm_convert(info)

    # If the machine is powered of, we do not get data
    powerstate = data["runtime.powerState"][0]
    if powerstate != "poweredOn":
        raise MKCounterWrapped("VM is %s, skipping this check" % powerstate)


    try:
        #consumed host memory
        host_memory_usage   = savefloat(data["summary.quickStats.hostMemoryUsage"][0]) * 1024 * 1024
        #active guest memory
        guest_memory_usage  = savefloat(data["summary.quickStats.guestMemoryUsage"][0]) * 1024 * 1024
        #size of the balloon driver in the VM
        ballooned_memory    = savefloat(data["summary.quickStats.balloonedMemory"][0]) * 1024 * 1024
        #The portion of memory, in MB, that is granted to this VM from non-shared host memor(musst not be set)
        shared_memory       = savefloat(data["summary.quickStats.sharedMemory"][0]) * 1024 * 1024
        #The portion of memory, in MB, that is granted to this VM from host memory that is shared between VMs.
        private_memory      = savefloat(data.get("summary.quickStats.privateMemory",[0])[0]) * 1024 * 1024
    except:
        raise MKCounterWrapped("Hostsystem did not provide memory information (reason may be high load)")

    perf = [
        ("host",      host_memory_usage ),
        ("guest",     guest_memory_usage ),
        ("ballooned", ballooned_memory ),
        ("shared",    shared_memory ),
        ("private",   private_memory ),
    ]

    message = "Host: %s, Guest: %s, " \
              "Ballooned: %s, Private: %s, Shared: %s" % \
    (get_bytes_human_readable(host_memory_usage), \
     get_bytes_human_readable(guest_memory_usage), get_bytes_human_readable(ballooned_memory), \
     get_bytes_human_readable(private_memory), get_bytes_human_readable(shared_memory) )
    return(0, message, perf)


check_info['esx_vsphere_vm.mem_usage'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_mem,
  "check_function"      : check_esx_vsphere_vm_mem,
  "service_description" : "ESX Memory",
  "has_perfdata"        : True
}

#.
#   .--Name----------------------------------------------------------------.
#   |                     _   _                                            |
#   |                    | \ | | __ _ _ __ ___   ___                       |
#   |                    |  \| |/ _` | '_ ` _ \ / _ \                      |
#   |                    | |\  | (_| | | | | | |  __/                      |
#   |                    |_| \_|\__,_|_| |_| |_|\___|                      |
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_esx_vsphere_vm_name(info):
    data = esx_vsphere_vm_convert(info).keys()
    if 'name' in data:
        return [(None, None)]

def check_esx_vsphere_vm_name(_no_item, _no_params, info):
    data = esx_vsphere_vm_convert(info)
    return(0, " ".join(data['name']))


check_info['esx_vsphere_vm.name'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_name,
  "check_function"      : check_esx_vsphere_vm_name,
  "service_description" : "ESX Name",
}



#.
#   .--Runtime Host--------------------------------------------------------.
#   |    ____              _   _                  _   _           _        |
#   |   |  _ \ _   _ _ __ | |_(_)_ __ ___   ___  | | | | ___  ___| |_      |
#   |   | |_) | | | | '_ \| __| | '_ ` _ \ / _ \ | |_| |/ _ \/ __| __|     |
#   |   |  _ <| |_| | | | | |_| | | | | | |  __/ |  _  | (_) \__ \ |_      |
#   |   |_| \_\\__,_|_| |_|\__|_|_| |_| |_|\___| |_| |_|\___/|___/\__|     |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_esx_vsphere_vm_running_on(info):
    data = esx_vsphere_vm_convert(info)
    if 'runtime.host' in data:
        return [(None, None)]
    return []

def check_esx_vsphere_vm_running_on(no_item, no_params, info):
    data = esx_vsphere_vm_convert(info)

    running_on = data.get("runtime.host")
    if not running_on:
        return 3, "Runtime host information is missing"

    return 0, "Running on %s" % running_on[0]

check_info['esx_vsphere_vm.running_on'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_running_on,
  "check_function"      : check_esx_vsphere_vm_running_on,
  "service_description" : "ESX Hostsystem",
}


#.
#   .--VM Datastores--------------------------------------------------------.
#   |    __     ____  __   ____        _            _                      |
#   |    \ \   / /  \/  | |  _ \  __ _| |_ __ _ ___| |_ ___  _ __ ___      |
#   |     \ \ / /| |\/| | | | | |/ _` | __/ _` / __| __/ _ \| '__/ _ \     |
#   |      \ V / | |  | | | |_| | (_| | || (_| \__ \ || (_) | | |  __/     |
#   |       \_/  |_|  |_| |____/ \__,_|\__\__,_|___/\__\___/|_|  \___|     |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_esx_vsphere_vm_datastores(info):
    data = esx_vsphere_vm_convert(info)
    # Right now we only handle one datastore per vm
    if 'config.datastoreUrl' in data:
        return [(None, None)]
    return []

def check_esx_vsphere_vm_datastores(no_item, no_params, info):
    data = esx_vsphere_vm_convert(info)

    datastore_urls = data.get("config.datastoreUrl")
    if not datastore_urls:
        return 3, "Datastore information is missing"

    output = []
    for datastore_url in " ".join(datastore_urls).split("\t"):
        datastore_url = datastore_url.split("|")
        output_store = []

        # datastore_url looks like
        #['url /vmfs/volumes/513df1e9-12fd7366-ac5a-e41f13e69eaa',
        # 'uncommitted 51973812224',
        # 'name zmucvm99-lds',
        # 'type VMFS',
        # 'accessible true',
        # 'capacity 578478407680',
        # 'freeSpace 68779245568']

        # Convert datastore_url to dict
        datastore_dict = dict(map(lambda x: x.split(" ", 1), datastore_url))

        capacity = saveint(datastore_dict.get("capacity", 0)) * 1.0
        if capacity:
            free_perc = int(datastore_dict.get("freeSpace", 0)) / capacity * 100
        else:
            free_perc = 0.0

        output_store = "Stored on %s (%s/%0.1f%% free)" %\
                             (datastore_dict.get("name"),
                              get_bytes_human_readable(capacity),
                              free_perc)
        output.append(output_store)
    return 0, ", ".join(output)

check_info['esx_vsphere_vm.datastores'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_datastores,
  "check_function"      : check_esx_vsphere_vm_datastores,
  "service_description" : "ESX Datastores",
}

#.
#   .--GuestTools----------------------------------------------------------.
#   |            ____                 _  _____           _                 |
#   |           / ___|_   _  ___  ___| ||_   _|__   ___ | |___             |
#   |          | |  _| | | |/ _ \/ __| __|| |/ _ \ / _ \| / __|            |
#   |          | |_| | |_| |  __/\__ \ |_ | | (_) | (_) | \__ \            |
#   |           \____|\__,_|\___||___/\__||_|\___/ \___/|_|___/            |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_esx_vsphere_vm_guest_tools(info):
    data = esx_vsphere_vm_convert(info)
    if 'guest.toolsVersionStatus' in data:
        return [(None, None)]

def check_esx_vsphere_vm_guest_tools(_no_item, params, info):
    data = esx_vsphere_vm_convert(info)

    vm_status = data['guest.toolsVersionStatus'][0]

    guest_tools_map = {
        "guestToolsCurrent":      (0, "VMware Tools is installed, and the version is current"),
        "guestToolsNeedUpgrade":  (1, "VMware Tools is installed, but the version is not current"),
        "guestToolsNotInstalled": (2, "VMware Tools has never been installed"),
        "guestToolsUnmanaged":    (1, "VMware Tools is installed, but it is not managed by VMWare")
    }
    state, info = guest_tools_map.get(vm_status, (3, "Unknown status for VMware Tools"))

    if params:
        state = params.get(vm_status, state)

    return state, info

check_info['esx_vsphere_vm.guest_tools'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_guest_tools,
  "check_function"      : check_esx_vsphere_vm_guest_tools,
  "service_description" : "ESX Guest Tools",
  "group"               : "vm_guest_tools"
}


#.
#   .--Heartbeat-----------------------------------------------------------.
#   |           _   _                 _   _                _               |
#   |          | | | | ___  __ _ _ __| |_| |__   ___  __ _| |_             |
#   |          | |_| |/ _ \/ _` | '__| __| '_ \ / _ \/ _` | __|            |
#   |          |  _  |  __/ (_| | |  | |_| |_) |  __/ (_| | |_             |
#   |          |_| |_|\___|\__,_|_|   \__|_.__/ \___|\__,_|\__|            |
#   |                                                                      |
#   '----------------------------------------------------------------------'

# Possible values (this list is taken from the official documentation)
#    gray - VMware Tools are not installed or not running.
#    red - No heartbeat. Guest operating system may have stopped responding.
#    yellow - Intermittent heartbeat. May be due to guest load.
#    green - Guest operating system is responding normally.
#
def inventory_esx_vsphere_vm_hb_status(info):
    data = esx_vsphere_vm_convert(info)
    if 'guestHeartbeatStatus' in data:
        return [(None, None)]

def check_esx_vsphere_vm_hb_status(_no_item, params, info):
    data = esx_vsphere_vm_convert(info)

    vm_status = data['guestHeartbeatStatus'][0]
    state = 3

    vm_heartbeat_map = { "gray"   : (1, "heartbeat_no_tools"),
                         "green"  : (0, "heartbeat_ok"),
                         "red"    : (2, "heartbeat_missing"),
                         "yellow" : (1, "heartbeat_intermittend") }
    if vm_status in vm_heartbeat_map:
        if params:
            state = params.get(vm_heartbeat_map.get(vm_status)[1], 3)
        else:
            state = vm_heartbeat_map.get(vm_status)[0]
        if vm_status == 'gray':
            return state, "No VMWare Tools installed, outdated or not running"
        else:
            return state, "Heartbeat status is %s" % vm_status
    else:
        return 3, "Unknown heartbeat status %s" % vm_status



check_info['esx_vsphere_vm.heartbeat'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_hb_status,
  "check_function"      : check_esx_vsphere_vm_hb_status,
  "service_description" : "ESX Heartbeat",
  "group"               : "vm_heartbeat"
}

#.
#   .--CPU-----------------------------------------------------------------.
#   |                           ____ ____  _   _                           |
#   |                          / ___|  _ \| | | |                          |
#   |                         | |   | |_) | | | |                          |
#   |                         | |___|  __/| |_| |                          |
#   |                          \____|_|    \___/                           |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

# <<<esx_vsphere_vm>>>
# config.hardware.numCPU 8
# config.hardware.numCoresPerSocket 2
# summary.quickStats.overallCpuUsage 8

def inventory_esx_vsphere_vm_cpu(info):
    data = esx_vsphere_vm_convert(info)
    if 'summary.quickStats.overallCpuUsage' in data:
        return [(None, None)]

def check_esx_vsphere_vm_cpu(_no_item, _no_params, info):
    data = esx_vsphere_vm_convert(info)
    # VMs that are currently down do not have this entry
    if 'summary.quickStats.overallCpuUsage' not in data:
        raise MKCounterWrapped("No information about CPU usage. VM is probably powered off.")

    usage_mhz = int(data['summary.quickStats.overallCpuUsage'][0])
    cpus = int(data['config.hardware.numCPU'][0])
    return 0, "demand is %.3f Ghz, %d virtual CPUs" % (usage_mhz / 1000.0, cpus), [ ("demand", usage_mhz) ]



check_info['esx_vsphere_vm.cpu'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_cpu,
  "check_function"      : check_esx_vsphere_vm_cpu,
  "service_description" : "ESX CPU",
  "has_perfdata"        : True,
}


#   .--Snapshots-----------------------------------------------------------.
#   |           ____                        _           _                  |
#   |          / ___| _ __   __ _ _ __  ___| |__   ___ | |_ ___            |
#   |          \___ \| '_ \ / _` | '_ \/ __| '_ \ / _ \| __/ __|           |
#   |           ___) | | | | (_| | |_) \__ \ | | | (_) | |_\__ \           |
#   |          |____/|_| |_|\__,_| .__/|___/_| |_|\___/ \__|___/           |
#   |                            |_|                                       |
#   +----------------------------------------------------------------------+

# <<<esx_vsphere_vm>>>
# snapshot.rootSnapshotList 1 1363596734 poweredOff 20130318_105600_snapshot_LinuxI|2 1413977827 poweredOn LinuxI Testsnapshot


def inventory_esx_vsphere_vm_snapshots(info):
    return [(None, {})]

def check_esx_vsphere_vm_snapshots(_no_item, params, info):
    data = esx_vsphere_vm_convert(info)

    if 'snapshot.rootSnapshotList' not in data:
        yield 0, "No snapshots found"
    else:
        last_snapshot  = None
        powered_on_snapshot = None
        snapshots = map(lambda x: x.split(" ", 3), " ".join(data["snapshot.rootSnapshotList"]).split("|"))
        snapshots = map(lambda x: (int(x[0]), int(x[1]), x[2], x[3]) , snapshots)
        yield 0, "Number of Snapshots %d" % len(snapshots)

        if len(snapshots) > 0:
            last_snapshot = snapshots[0]
            for snapshot in snapshots:
                if snapshot[1] > last_snapshot[1]:
                    last_snapshot = snapshot
                if snapshot[2] == "poweredOn":
                    powered_on_snapshot = snapshot

            yield 0, "Powered On: %s" % (powered_on_snapshot and powered_on_snapshot[3] or "None")

            perfdata = []
            if params.get("age"):
                warn, crit = params["age"]
                snapshot_age = time.time() - last_snapshot[1]
                if snapshot_age > crit:
                    yield 2, "Snapshot is older than %s" % get_age_human_readable(snapshot_age)
                elif snapshot_age > warn:
                    yield 1, "Snapshot is older than %s" % get_age_human_readable(snapshot_age)
                perfdata = [("age", last_snapshot[1], warn, crit)]
            else:
                perfdata = [("age", last_snapshot[1])]

            yield 0, "Last Snapshot: %s %s" % (last_snapshot[3],
                                               time.strftime("%D %H:%M",time.localtime(last_snapshot[1]))),\
                                               perfdata



check_info['esx_vsphere_vm.snapshots'] = {
  "inventory_function"  : inventory_esx_vsphere_vm_snapshots,
  "check_function"      : check_esx_vsphere_vm_snapshots,
  "service_description" : "ESX Snapshots",
  "group"               : "vm_snapshots",
  "has_perfdata"        : True,
}
