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

# This agent version uses predefined soap inquiries which are sent
# to an esx host system. # Unlike the classic agent the reported data
# isn't processed through the pysphere module.
# Instead a simple string process approach is used which drastically
# reduces the CPU load for this agent

# pylint: disable=broad-except

import datetime
import errno
import getopt
import httplib
import os
import re
import socket
import sys
import time
from typing import Optional, Text  # pylint: disable=unused-import
from xml.dom import minidom  # type: ignore

from dateutil import tz
from pathlib2 import Path

import cmk.utils.paths
from cmk.utils.exceptions import MKGeneralException


class MKQueryServerException(Exception):
    pass


# Great article how to get additional information
# http://www.veeam.com/kb1007

telegram_list = {
       "systeminfo":
         '<ns1:RetrieveServiceContent xsi:type="ns1:RetrieveServiceContentRequestType">'\
         '<ns1:_this type="ServiceInstance">ServiceInstance</ns1:_this></ns1:RetrieveServiceContent>',

       "login":
         '<ns1:Login xsi:type="ns1:LoginRequestType"><ns1:_this type="SessionManager">%(sessionManager)s</ns1:_this>'\
         '<ns1:userName>%(username)s</ns1:userName><ns1:password>%(password)s</ns1:password></ns1:Login>',

       "systemtime":
         '<ns1:CurrentTime xsi:type="ns1:CurrentTimeRequestType">'\
         '<ns1:_this type="ServiceInstance">ServiceInstance</ns1:_this></ns1:CurrentTime>',

       "hostsystems":
         '<ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">'\
         '<ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this><ns1:specSet>'\
         '<ns1:propSet><ns1:type>HostSystem</ns1:type><ns1:pathSet>name</ns1:pathSet></ns1:propSet>'\
         '<ns1:objectSet><ns1:obj type="Folder">%(rootFolder)s</ns1:obj><ns1:skip>false</ns1:skip>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>visitFolders</ns1:name>'\
           '<ns1:type>Folder</ns1:type><ns1:path>childEntity</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToHf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToVmf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToH</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToDs</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>hToVm</ns1:name></ns1:selectSet>'\
         '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToVmf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>vmFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToDs</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>datastore</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToHf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>hostFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToH</ns1:name><ns1:type>ComputeResource</ns1:type>'\
         '<ns1:path>host</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToRp</ns1:name><ns1:type>ComputeResource</ns1:type>'\
         '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
         '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
         '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToRp</ns1:name><ns1:type>ResourcePool</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>hToVm</ns1:name><ns1:type>HostSystem</ns1:type>'\
           '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToVm</ns1:name><ns1:type>ResourcePool</ns1:type>'\
         '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '</ns1:objectSet></ns1:specSet><ns1:options></ns1:options></ns1:RetrievePropertiesEx>',

     "datastores":
         '<ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">'\
         '<ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this><ns1:specSet>'\
         '<ns1:propSet><ns1:type>Datastore</ns1:type><ns1:pathSet>name</ns1:pathSet>'\
         '<ns1:pathSet>summary.freeSpace</ns1:pathSet>'\
         '<ns1:pathSet>summary.capacity</ns1:pathSet>'\
         '<ns1:pathSet>summary.uncommitted</ns1:pathSet>'\
         '<ns1:pathSet>summary.url</ns1:pathSet>'\
         '<ns1:pathSet>summary.accessible</ns1:pathSet>'\
         '<ns1:pathSet>summary.type</ns1:pathSet>'\
         '<ns1:pathSet>summary.maintenanceMode</ns1:pathSet></ns1:propSet>'\
         '<ns1:objectSet><ns1:obj type="Folder">%(rootFolder)s</ns1:obj><ns1:skip>false</ns1:skip>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>visitFolders</ns1:name>'\
           '<ns1:type>Folder</ns1:type><ns1:path>childEntity</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToHf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToVmf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToH</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToDs</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>hToVm</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToVmf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>vmFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToDs</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>datastore</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToHf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>hostFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToH</ns1:name><ns1:type>ComputeResource</ns1:type>'\
         '<ns1:path>host</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToRp</ns1:name><ns1:type>ComputeResource</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
         '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToRp</ns1:name><ns1:type>ResourcePool</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>hToVm</ns1:name><ns1:type>HostSystem</ns1:type>'\
           '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToVm</ns1:name><ns1:type>ResourcePool</ns1:type>'\
         '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '</ns1:objectSet></ns1:specSet><ns1:options></ns1:options></ns1:RetrievePropertiesEx>',

     "licensesused": """
        <ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">
          <ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this>
          <ns1:specSet>
            <ns1:propSet>
              <ns1:type>LicenseManager</ns1:type>
              <all>0</all>
              <ns1:pathSet>licenses</ns1:pathSet>
            </ns1:propSet>
            <ns1:objectSet>
              <ns1:obj type="LicenseManager">%(licenseManager)s</ns1:obj>
            </ns1:objectSet>
          </ns1:specSet>
          <ns1:options/>
        </ns1:RetrievePropertiesEx>
    """,

     "perfcountersummary":
         '<ns1:QueryPerfProviderSummary xsi:type="ns1:QueryPerfProviderSummaryRequestType">'\
         '<ns1:_this type="PerformanceManager">%(perfManager)</ns1:_this>'\
         '<ns1:entity type="HostSystem">%(esxhost)s</ns1:entity></ns1:QueryPerfProviderSummary>',

     "perfcountersyntax":
         '<ns1:QueryPerfCounter xsi:type="ns1:QueryPerfCounterRequestType">'\
         '<ns1:_this type="PerformanceManager">%(perfManager)s</ns1:_this>%(counters)s</ns1:QueryPerfCounter>',

     "perfcounteravail":
         '<ns1:QueryAvailablePerfMetric xsi:type="ns1:QueryAvailablePerfMetricRequestType">'\
         '<ns1:_this type="PerformanceManager">%(perfManager)s</ns1:_this>'\
         '<ns1:entity type="HostSystem">%(esxhost)s</ns1:entity>'\
         '<ns1:intervalId>20</ns1:intervalId></ns1:QueryAvailablePerfMetric>',

     "perfcounterdata":
         '<ns1:QueryPerf xsi:type="ns1:QueryPerfRequestType"><ns1:_this type="PerformanceManager">%(perfManager)s</ns1:_this>'\
         '<ns1:querySpec><ns1:entity type="HostSystem">%(esxhost)s</ns1:entity><ns1:maxSample>%(samples)s</ns1:maxSample>%(counters)s'\
         '<ns1:intervalId>20</ns1:intervalId></ns1:querySpec></ns1:QueryPerf>',

     "networksystem":
         '<ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">'\
         '<ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this><ns1:specSet>'\
         '<ns1:propSet><ns1:type>HostNetworkSystem</ns1:type><all>0</all>'\
         '<ns1:pathSet>networkInfo</ns1:pathSet></ns1:propSet>'\
         '<ns1:objectSet><ns1:obj type="HostNetworkSystem">networkSystem</ns1:obj>'\
         '</ns1:objectSet></ns1:specSet><ns1:options></ns1:options></ns1:RetrievePropertiesEx>',

     "esxhostdetails":
         '<ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">'
         '<ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this><ns1:specSet><ns1:propSet>'\
         '<ns1:type>HostSystem</ns1:type>'\
         '<ns1:pathSet>summary.quickStats.overallMemoryUsage</ns1:pathSet>'\
         '<ns1:pathSet>hardware.cpuPkg</ns1:pathSet>'\
#         '<ns1:pathSet>hardware.pciDevice</ns1:pathSet>'\
         '<ns1:pathSet>runtime.powerState</ns1:pathSet>'\
         '<ns1:pathSet>summary.quickStats.overallCpuUsage</ns1:pathSet>'\
         '<ns1:pathSet>hardware.biosInfo.biosVersion</ns1:pathSet>'\
         '<ns1:pathSet>hardware.biosInfo.releaseDate</ns1:pathSet>'\
         '<ns1:pathSet>hardware.cpuInfo.hz</ns1:pathSet>'\
         '<ns1:pathSet>hardware.cpuInfo.numCpuThreads</ns1:pathSet>'\
         '<ns1:pathSet>hardware.cpuInfo.numCpuPackages</ns1:pathSet>'\
         '<ns1:pathSet>hardware.cpuInfo.numCpuCores</ns1:pathSet>'\
         '<ns1:pathSet>config.storageDevice.multipathInfo</ns1:pathSet>'\
         '<ns1:pathSet>hardware.systemInfo.model</ns1:pathSet>'\
         '<ns1:pathSet>hardware.systemInfo.uuid</ns1:pathSet>'\
         '<ns1:pathSet>hardware.systemInfo.otherIdentifyingInfo</ns1:pathSet>'\
         '<ns1:pathSet>hardware.systemInfo.vendor</ns1:pathSet>'\
         '<ns1:pathSet>name</ns1:pathSet>'\
         '<ns1:pathSet>overallStatus</ns1:pathSet>'\
         '<ns1:pathSet>runtime.healthSystemRuntime.systemHealthInfo.numericSensorInfo</ns1:pathSet>'\
         '<ns1:pathSet>runtime.healthSystemRuntime.hardwareStatusInfo.storageStatusInfo</ns1:pathSet>'\
         '<ns1:pathSet>runtime.healthSystemRuntime.hardwareStatusInfo.cpuStatusInfo</ns1:pathSet>'\
         '<ns1:pathSet>runtime.healthSystemRuntime.hardwareStatusInfo.memoryStatusInfo</ns1:pathSet>'\
         '<ns1:pathSet>runtime.inMaintenanceMode</ns1:pathSet>'\
         '<ns1:pathSet>hardware.memorySize</ns1:pathSet></ns1:propSet>'\
         '<ns1:objectSet><ns1:obj type="Folder">%(rootFolder)s</ns1:obj><ns1:skip>false</ns1:skip>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>visitFolders</ns1:name>'\
           '<ns1:type>Folder</ns1:type><ns1:path>childEntity</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToHf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToVmf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToH</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToDs</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>hToVm</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToVmf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>vmFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToDs</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>datastore</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToHf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>hostFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToH</ns1:name><ns1:type>ComputeResource</ns1:type>'\
           '<ns1:path>host</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToRp</ns1:name><ns1:type>ComputeResource</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToRp</ns1:name><ns1:type>ResourcePool</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>hToVm</ns1:name><ns1:type>HostSystem</ns1:type>'\
           '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToVm</ns1:name><ns1:type>ResourcePool</ns1:type>'\
           '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '</ns1:objectSet></ns1:specSet><ns1:options></ns1:options></ns1:RetrievePropertiesEx>',

     "vmdetails":
         '<ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">'\
         '<ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this><ns1:specSet><ns1:propSet>'\
         '<ns1:type>VirtualMachine</ns1:type>'\
           '<ns1:pathSet>summary.quickStats.consumedOverheadMemory</ns1:pathSet>'\
           '<ns1:pathSet>config.hardware.numCPU</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.overallCpuDemand</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.distributedCpuEntitlement</ns1:pathSet>'\
           '<ns1:pathSet>runtime.host</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.distributedMemoryEntitlement</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.uptimeSeconds</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.sharedMemory</ns1:pathSet>'\
           '<ns1:pathSet>config.hardware.memoryMB</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.privateMemory</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.balloonedMemory</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.staticMemoryEntitlement</ns1:pathSet>'\
           '<ns1:pathSet>runtime.powerState</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.overallCpuUsage</ns1:pathSet>'\
           '<ns1:pathSet>config.hardware.numCoresPerSocket</ns1:pathSet>'\
           '<ns1:pathSet>config.hardware.device</ns1:pathSet>'\
           '<ns1:pathSet>config.template</ns1:pathSet>'\
           '<ns1:pathSet>guest.toolsVersion</ns1:pathSet>'\
           '<ns1:pathSet>guestHeartbeatStatus</ns1:pathSet>'\
           '<ns1:pathSet>name</ns1:pathSet>'\
           '<ns1:pathSet>summary.guest.hostName</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.compressedMemory</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.swappedMemory</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.guestMemoryUsage</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.staticCpuEntitlement</ns1:pathSet>'\
           '<ns1:pathSet>summary.quickStats.hostMemoryUsage</ns1:pathSet>'\
           '<ns1:pathSet>snapshot.rootSnapshotList</ns1:pathSet>'\
           '<ns1:pathSet>config.datastoreUrl</ns1:pathSet>'\
           '<ns1:pathSet>guest.toolsVersionStatus</ns1:pathSet></ns1:propSet>'\
         '<ns1:objectSet><ns1:obj type="Folder">%(rootFolder)s</ns1:obj><ns1:skip>false</ns1:skip>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>visitFolders</ns1:name>'\
           '<ns1:type>Folder</ns1:type><ns1:path>childEntity</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToHf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToVmf</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToH</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>crToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>dcToDs</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>hToVm</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToVmf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>vmFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToDs</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>datastore</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>dcToHf</ns1:name><ns1:type>Datacenter</ns1:type>'\
           '<ns1:path>hostFolder</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToH</ns1:name><ns1:type>ComputeResource</ns1:type>'\
         '<ns1:path>host</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>crToRp</ns1:name><ns1:type>ComputeResource</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToRp</ns1:name><ns1:type>ResourcePool</ns1:type>'\
           '<ns1:path>resourcePool</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>rpToRp</ns1:name></ns1:selectSet>'\
           '<ns1:selectSet><ns1:name>rpToVm</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec">'\
           '<ns1:name>hToVm</ns1:name><ns1:type>HostSystem</ns1:type>'\
           '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip>'\
           '<ns1:selectSet><ns1:name>visitFolders</ns1:name></ns1:selectSet></ns1:selectSet>'\
         '<ns1:selectSet xsi:type="ns1:TraversalSpec"><ns1:name>rpToVm</ns1:name><ns1:type>ResourcePool</ns1:type>'\
         '<ns1:path>vm</ns1:path><ns1:skip>false</ns1:skip></ns1:selectSet>'\
         '</ns1:objectSet></ns1:specSet><ns1:options></ns1:options></ns1:RetrievePropertiesEx>',

     "continuetoken":
         '<ns1:ContinueRetrievePropertiesEx xsi:type="ns1:ContinueRetrievePropertiesExRequestType">'\
         '<ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this><ns1:token>%(token)s</ns1:token></ns1:ContinueRetrievePropertiesEx>',

     "datacenters": """
  <ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">
    <ns1:_this type="PropertyCollector">%(propertyCollector)s</ns1:_this>
    <ns1:specSet>
      <ns1:propSet>
        <ns1:type>Datacenter</ns1:type>
        <ns1:pathSet>name</ns1:pathSet>
      </ns1:propSet>
      <ns1:objectSet>
        <ns1:obj type="Folder">%(rootFolder)s</ns1:obj>
        <ns1:skip>false</ns1:skip>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>visitFolders</ns1:name>
          <ns1:type>Folder</ns1:type>
          <ns1:path>childEntity</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToHf</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToVmf</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>crToH</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>crToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToDs</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>hToVm</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToVmf</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>vmFolder</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToDs</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>datastore</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToHf</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>hostFolder</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>crToH</ns1:name>
          <ns1:type>ComputeResource</ns1:type>
          <ns1:path>host</ns1:path>
          <ns1:skip>false</ns1:skip>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>crToRp</ns1:name>
          <ns1:type>ComputeResource</ns1:type>
          <ns1:path>resourcePool</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>rpToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>rpToRp</ns1:name>
          <ns1:type>ResourcePool</ns1:type>
          <ns1:path>resourcePool</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>rpToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>hToVm</ns1:name>
          <ns1:type>HostSystem</ns1:type>
          <ns1:path>vm</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>rpToVm</ns1:name>
          <ns1:type>ResourcePool</ns1:type>
          <ns1:path>vm</ns1:path>
          <ns1:skip>false</ns1:skip>
        </ns1:selectSet>
      </ns1:objectSet>
    </ns1:specSet>
    <ns1:options/>
  </ns1:RetrievePropertiesEx>""",

     "clustersofdatacenter": """
  <ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">
    <ns1:_this type="PropertyCollector">propertyCollector</ns1:_this>
    <ns1:specSet>
      <ns1:propSet>
        <ns1:type>ClusterComputeResource</ns1:type>
        <ns1:pathSet>name</ns1:pathSet>
      </ns1:propSet>
      <ns1:objectSet>
        <ns1:obj type="Datacenter">%(datacenter)s</ns1:obj>
        <ns1:skip>false</ns1:skip>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>visitFolders</ns1:name>
          <ns1:type>Folder</ns1:type>
          <ns1:path>childEntity</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToHf</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToVmf</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>crToH</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>crToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToDs</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>hToVm</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToVmf</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>vmFolder</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToDs</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>datastore</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToHf</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>hostFolder</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>crToH</ns1:name>
          <ns1:type>ComputeResource</ns1:type>
          <ns1:path>host</ns1:path>
          <ns1:skip>false</ns1:skip>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>crToRp</ns1:name>
          <ns1:type>ComputeResource</ns1:type>
          <ns1:path>resourcePool</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>rpToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>rpToRp</ns1:name>
          <ns1:type>ResourcePool</ns1:type>
          <ns1:path>resourcePool</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>rpToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>hToVm</ns1:name>
          <ns1:type>HostSystem</ns1:type>
          <ns1:path>vm</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>rpToVm</ns1:name>
          <ns1:type>ResourcePool</ns1:type>
          <ns1:path>vm</ns1:path>
          <ns1:skip>false</ns1:skip>
        </ns1:selectSet>
      </ns1:objectSet>
    </ns1:specSet>
    <ns1:options/>
  </ns1:RetrievePropertiesEx>""",

     "esxhostsofcluster":
  """<ns1:RetrievePropertiesEx xsi:type="ns1:RetrievePropertiesExRequestType">
    <ns1:_this type="PropertyCollector">propertyCollector</ns1:_this>
    <ns1:specSet>
      <ns1:propSet>
        <ns1:type>HostSystem</ns1:type>
        <ns1:pathSet>name</ns1:pathSet>
      </ns1:propSet>
      <ns1:objectSet>
        <ns1:obj type="ClusterComputeResource">%(clustername)s</ns1:obj>
        <ns1:skip>false</ns1:skip>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>visitFolders</ns1:name>
          <ns1:type>Folder</ns1:type>
          <ns1:path>childEntity</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToHf</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToVmf</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>crToH</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>crToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>dcToDs</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>hToVm</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToVmf</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>vmFolder</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToDs</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>datastore</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>dcToHf</ns1:name>
          <ns1:type>Datacenter</ns1:type>
          <ns1:path>hostFolder</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>crToH</ns1:name>
          <ns1:type>ComputeResource</ns1:type>
          <ns1:path>host</ns1:path>
          <ns1:skip>false</ns1:skip>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>crToRp</ns1:name>
          <ns1:type>ComputeResource</ns1:type>
          <ns1:path>resourcePool</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>rpToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>rpToRp</ns1:name>
          <ns1:type>ResourcePool</ns1:type>
          <ns1:path>resourcePool</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>rpToRp</ns1:name>
          </ns1:selectSet>
          <ns1:selectSet>
            <ns1:name>rpToVm</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>hToVm</ns1:name>
          <ns1:type>HostSystem</ns1:type>
          <ns1:path>vm</ns1:path>
          <ns1:skip>false</ns1:skip>
          <ns1:selectSet>
            <ns1:name>visitFolders</ns1:name>
          </ns1:selectSet>
        </ns1:selectSet>
        <ns1:selectSet xsi:type="ns1:TraversalSpec">
          <ns1:name>rpToVm</ns1:name>
          <ns1:type>ResourcePool</ns1:type>
          <ns1:path>vm</ns1:path>
          <ns1:skip>false</ns1:skip>
        </ns1:selectSet>
      </ns1:objectSet>
    </ns1:specSet>
    <ns1:options/>
  </ns1:RetrievePropertiesEx>"""
}

REQUESTED_COUNTERS_KEYS = (
    'disk.numberRead',
    'disk.numberWrite',
    'disk.read',
    'disk.write',
    'disk.deviceLatency',
    'net.usage',
    'net.packetsRx',
    'net.packetsTx',
    'net.received',
    'net.transmitted',
    'net.droppedRx',
    'net.droppedTx',
    'net.bytesRx',
    'net.bytesTx',
    'net.broadcastRx',
    'net.broadcastTx',
    'net.multicastRx',
    'net.multicastTx',
    'net.errorsRx',
    'net.errorsTx',
    'net.unknownProtos',
    'sys.uptime',
    'sys.resourceMemConsumed',
    'datastore.read',
    'datastore.write',
    'datastore.sizeNormalizedDatastoreLatency',
    'datastore.datastoreReadIops',
    'datastore.datastoreWriteIops',
)


def usage():
    sys.stderr.write("""Check_MK vSphere Agent

USAGE: agent_vsphere [OPTIONS] HOST
       agent_vsphere -h

ARGUMENTS:
  HOST                          Host name or IP address of VMWare HostSystem

OPTIONS:
  -h, --help                    Show this help message and exit
  -u USER, --user USER          Username for vSphere login
  -s SECRET, --secret SECRET    Secret/Password for vSphere login
  -D, --direct                  Assume a directly queried host system (no vCenter). In
                                This we expect data about only one HostSystem to be
                                found and do not create piggy host data for that host.
  -P                            Skip placeholder virtualmachines. These backup vms are created
                                by the Site Recovery Manager (SRM) and are identified by not
                                having any assigned virtual disks.
  -p, --port port               Alternative port number (default is 443 for the https connection)
  --no-cert-check               Disables the checking of the servers ssl certificate
  --pysphere                    Fallback to old pysphere based special agent. It supports
                                ESX 4.1 but is very slow.
  -H, --hostname                Specify a hostname. This is neccessary if this is
                                different from HOST. It is being used when outputting
                                the hosts power state.
  -a, --agent                   Also retrieve data from the normal Check_MK Agent.
                                This makes sense if you query a vCenter that is
                                Installed on a Windows host that you also want to
                                Monitor with Check_MK.
  -t, --timeout SECS            Set the network timeout to vSphere to SECS seconds.
                                This is also used when connecting the agent (option -a).
                                Default is 60 seconds. Note: the timeout is not only
                                applied to the connection, but also to each individual
                                subquery.
  --debug                       Debug mode: let Python exceptions come through

  --tracefile FILENAME          Log all outgoing and incoming data into the given tracefile
  -i MODULES, --modules MODULES Modules to query. This is a comma separated list of
                                hostsystem, virtualmachine and storage. Default is to
                                query all modules.

  -S, --spaces HOW              How to handle spaces in hostnames. "cut": cut everyting
                                after the first space, "underscore": replace with
                                underscores. Default is "underscore".

  --vm_piggyname hostname|alias Here you can specify whether the virtual machines HOSTNAME or the ESX
                                system ALIAS name for this machine should be used on creating piggyback data

  --vm_pwr_display WHERE        Specifies where the virtual machines power state should be shown
                                Default (no option) is on the queried vCenter or ESX-Host
                                Possible WHERE options: * esxhost : show on ESX host
                                                        * vm      : show on virtual machine
  --host_pwr_display WHERE      Specifies where the ESX hosts power state should be shown
                                Default (no option) is on the queried vCenter or ESX-Host
                                Possible WHERE options: * esxhost : show on ESX host
                                                        * vm      : show on virtual machine
  --snapshot_display WHERE      Specifies where the virtual machines snapshots should be shown
                                Default (no option) is on the VM
                                Possible WHERE options: * esxhost : show on ESX host
                                                        * vCenter : show on vCenter
""")


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


# TODO: Refactor to requests
class ESXConnection(object):
    """Encapsulates the API calls to the ESX system"""
    @staticmethod
    def _connect_to_server(address, no_cert, debug):
        """Initialize server connection"""
        try:
            netloc = ":".join(map(str, address))

            if no_cert:
                try:
                    import ssl
                    server_handle = httplib.HTTPSConnection(
                        netloc, context=ssl._create_unverified_context())
                except Exception:
                    server_handle = httplib.HTTPSConnection(netloc)
            else:
                server_handle = httplib.HTTPSConnection(netloc)

            if debug:
                sys.stderr.write("Connecting to %s..." % netloc)
                sys.stderr.flush()

            server_handle.connect()
            return server_handle
        except Exception as exc:
            if debug:
                raise
            raise MKGeneralException("Cannot connect to vSphere Server. Please check the IP and"
                                     " SSL certificate (if applicable) and try again. This error"
                                     " is not related to the login credentials."
                                     " Error message: %r" % exc)

    @staticmethod
    def _check_not_authenticated(text):
        if "NotAuthenticatedFault" in text:
            raise MKQueryServerException("No longer authenticated")

    def __init__(self, address, user, secret, no_cert, debug):
        super(ESXConnection, self).__init__()
        self._user = user
        self._secret = secret
        self._server_handle = self._connect_to_server(address, no_cert, debug)

        self._host_cookie_path = Path("%s/agents/agent_vsphere/cookie.%s" %
                                      (cmk.utils.paths.tmp_dir, address[0]))

        self.last_cookie_access_time = None
        self.system_info = None
        self._server_cookie = None

        self.system_info = self._fetch_systeminfo()

    def _fetch_systeminfo(self):
        """Retrieve basic data, which requires no login"""
        system_info = {}

        # Globals of ESX System. These settings are available after the first "systeminfo" query
        systemfields = [
            "apiVersion",
            "name",
            "rootFolder",
            "perfManager",
            "sessionManager",
            "licenseManager",
            "propertyCollector",
            "version",
            "build",
            "vendor",
            "osType",
        ]

        reply_data = self.query_server(telegram_list["systeminfo"])
        for entry in systemfields:
            element = get_pattern("<%(entry)s.*>(.*)</%(entry)s>" % {"entry": entry}, reply_data)
            if element:
                system_info[entry] = element[0]

        if not system_info:
            raise MKGeneralException("Unable to get data from Web API" + "\n")

        return system_info

    def prepare_soapdata(self, payload, payload_params):
        if payload_params is None:
            payload_params = {}

        # Finalize payload
        if self.system_info:
            payload_params.update(self.system_info)
        soapdata = put_in_envelope(payload)
        return soapdata % payload_params, payload_params

    def send_soapdata(self, soapdata, payload_params):
        self._init_headers(soapdata)
        response_data = []

        if tracefile:
            tracefile.write("####   Sent  ####\n%s" % soapdata)
            time_sent = time.time()
        self._server_handle.send(soapdata)

        response = self._server_handle.getresponse()
        response_data.append(response.read())

        self._check_not_authenticated(response_data[0][:512])

        while True:
            # Look for a <token>0</token> field.
            # If it exists not all data was transmitted and we need to start a
            # ContinueRetrievePropertiesExResponse query...
            token = re.findall("<token>(.*)</token>", response_data[-1][:512])
            if token:
                payload_params.update({"token": token[0]})
                soapdata = put_in_envelope(telegram_list["continuetoken"]) % payload_params
                self._init_headers(soapdata)
                self._server_handle.send(soapdata)
                response = self._server_handle.getresponse()
                response_data.append(response.read())
                self._check_not_authenticated(response_data[-1][:512])
            else:
                break

        respose_text = "".join(response_data)
        if tracefile:
            timing_info = "Response took: %f" % (time.time() - time_sent)
            tracefile.write("\n#### Received #### %s\n%s\n" % (timing_info, respose_text))

        return response, respose_text

    def query_server(self, payload, payload_params=None):
        soapdata, payload_params = self.prepare_soapdata(payload, payload_params)
        _respose_object, response_text = self.send_soapdata(soapdata, payload_params)
        return response_text

    def _init_headers(self, soapdata):
        self._server_handle.putrequest("POST", "/sdk")
        self._server_handle.putheader("Content-Length", "%d" % len(soapdata))
        self._server_handle.putheader("Content-Type", 'text/xml; charset="utf-8"')
        self._server_handle.putheader("SOAPAction", "urn:vim25/5.0")
        self._server_handle.putheader("User-Agent",
                                      "VMware VI Client/5.0.0")  # TODO: set client version?
        if self._server_cookie:
            self._server_handle.putheader("Cookie", self._server_cookie)
        self._server_handle.endheaders()

    def login(self):
        if self._host_cookie_path.exists():
            # The cookie access time is required to determine the agents check interval
            # This is required later on by the performancecounters. Depending on the
            # interval we need more or less real-time samples
            # TODO: Decouple login secret and perfcounter timer
            self.last_cookie_access_time = self._host_cookie_path.stat().st_mtime
            os.utime(str(self._host_cookie_path),
                     None)  # Touch the cookie file, whenever it is accessed

            self._server_cookie = self._host_cookie_path.open(encoding="utf-8").read()
            return

        payload = telegram_list["login"]
        payload_params = {"username": encode_url(self._user), "password": encode_url(self._secret)}
        response, reply_data = self.send_soapdata(*self.prepare_soapdata(payload, payload_params))

        if "InvalidLogin" in reply_data:
            raise MKGeneralException("Cannot login to vSphere Server. Login response is not 'OK'."
                                     " Please check the credentials.")

        server_cookie = response.msg.get("Set-Cookie", "").decode("utf-8")

        if server_cookie:
            self._host_cookie_path.parent.mkdir(parents=True, exist_ok=True)  # pylint: disable=no-member
            with self._host_cookie_path.open("w", encoding="utf-8") as f_handle:
                f_handle.write(server_cookie)

        self._server_cookie = server_cookie
        return

    def delete_server_cookie(self):
        try:
            self._host_cookie_path.unlink()
        except OSError as exc:
            if exc.errno != errno.ENOENT:
                raise


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


def fetch_available_counters(connection, hostsystems):
    counters_available_by_host = {}
    for host in hostsystems:
        counter_avail_response = connection.query_server(telegram_list["perfcounteravail"],
                                                         payload_params={"esxhost": host})
        elements = get_pattern("<counterId>([0-9]*)</counterId><instance>([^<]*)",
                               counter_avail_response)

        data = counters_available_by_host.setdefault(host, {})
        for counter, instance in elements:
            data.setdefault(counter, []).append(instance)

    return counters_available_by_host


def fetch_counters_syntax(connection, counter_ids):

    counters_list = ["<ns1:counterId>%s</ns1:counterId>" % id_ for id_ in counter_ids]

    counters_syntax_response = connection.query_server(
        telegram_list["perfcountersyntax"], payload_params={"counters": "".join(counters_list)})

    elements = get_pattern('<returnval><key>(.*?)</key>.*?<key>(.*?)</key>.*?'\
                           '<key>(.*?)</key>.*?<key>(.*?)</key>.*?', counters_syntax_response)

    return {
        id_: {
            "key": ".".join((group, name)),
            "name": name,
            "group": group,
            "unit": unit
        } for id_, name, group, unit in elements
    }


def fetch_extra_interface_counters(connection):
    # Get additional interface counter info, this only works when querying ESX hosts
    # TODO: get this info from the vcenter
    if not opt_direct:
        return []

    net_extra_info = []
    networksystem_response = connection.query_server(telegram_list["networksystem"])
    nic_objects = get_pattern('<pnic><key>(.*?)</pnic>', networksystem_response)
    for nic in nic_objects:
        nic_if = get_pattern('(.*?)</key><device>(.*?)</device>(.*)<mac>(.*?)</mac>', nic)
        try:
            bandwidth_block = nic_if[0][2]
            bandwidth = get_pattern('</driver><linkSpeed><speedMb>(.*?)</speedMb>', bandwidth_block)
            net_extra_info.append("net.macaddress|%s|%s|mac" % (nic_if[0][1], nic_if[0][3]))
            if bandwidth:
                net_extra_info.append("net.bandwidth|%s|%s|bytes" %
                                      (nic_if[0][1], int(bandwidth[0]) * 1000000))
                net_extra_info.append("net.state|%s|1|state" % nic_if[0][1])
            else:
                net_extra_info.append("net.state|%s|2|state" % nic_if[0][1])
        except Exception:
            # TODO: This is way too generic. What should be catched here?
            return []

    return net_extra_info


def fetch_counters(connection, host, samples, counters_selected):
    counter_data = []
    for entry, instances in counters_selected:
        counter_data.extend(
            "<ns1:metricId><ns1:counterId>%s</ns1:counterId><ns1:instance>%s</ns1:instance></ns1:metricId>"
            % (entry, instance) for instance in instances)

    counter_data_response = connection.query_server(telegram_list["perfcounterdata"],
                                                    payload_params={
                                                        "esxhost": host,
                                                        "counters": "".join(counter_data),
                                                        "samples": samples
                                                    })

    # Python regex only supports up to 100 match groups in a regex..
    # We are only extracting the whole value line and split it later on
    # This is a perfect candidate for "Catastrophic Backtracking" :)
    # Someday we should replace all of these get_pattern calls with
    # one of these new and fancy xml parsers I've heard from
    elements = get_pattern(
        "<id><counterId>(.*?)</counterId><instance>(.*?)</instance></id>(%s)" %
        ("<value>.*?</value>" * samples), counter_data_response)
    counters_value = []
    for entry in elements:
        id_, instance, valuestring = entry
        values = get_pattern("<value>(.*?)</value>", valuestring)
        counters_value.append((id_, instance, values))

    return counters_value


def get_max_samples(connection):
    # Determine the needed number of real-time samples
    # One real-time sample is 20 seconds
    # -> With a check interval of 1 minute we need the last 3 samples
    # Longer check intervals require even more samples... n-minutes * 3
    # We set the max_samples hard cap to 180 (1 hour)
    # An ESX system does not offer more than one hour of real time samples, anyway.
    max_samples = 3
    if connection.last_cookie_access_time:
        timedelta = time.time() - connection.last_cookie_access_time
        max_samples = min(180, max(1, int(timedelta) / 20))
    return max_samples


def process_counters(connection, hostsystems, datastores):
    counters_available_by_host = fetch_available_counters(connection, hostsystems)
    counters_available_all = reduce(lambda x, y: x.union(y),
                                    counters_available_by_host.itervalues(), set())
    net_extra_info = fetch_extra_interface_counters(connection)
    counters_description = fetch_counters_syntax(connection, counters_available_all)
    max_samples = get_max_samples(connection)

    for host in hostsystems:
        if not opt_direct:
            output("<<<<%s>>>>" % convert_hostname(hostsystems[host]))

        counters_avail = counters_available_by_host[host]

        counters_selected = [
            (id_, instances)
            for id_, instances in counters_avail.iteritems()
            if counters_description.get(id_, {}).get("key") in REQUESTED_COUNTERS_KEYS
        ]

        counters_value = fetch_counters(connection, host, max_samples, counters_selected)

        output("<<<esx_vsphere_counters:sep(124)>>>")
        counters_output = {}
        for id_, instance, values in counters_value:
            desc = counters_description.get(id_)
            if not desc:
                continue
            counters_output[(desc["group"], desc["name"], instance)] = ('#'.join(values),
                                                                        desc["unit"])

        # Add datastore name to counters
        for key, values in datastores.items():
            counters_output[("datastore", "name", key)] = (values.get("name"), "string")

        for key in sorted(counters_output.keys()):
            value = counters_output[key]
            output("%s.%s|%s|%s|%s" % (key + value))

        for line in net_extra_info:
            output(line)

    if not opt_direct:
        output('<<<<>>>>')


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


def put_in_envelope(payload):
    return '<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" '\
           'xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" '\
           'xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" '\
           'xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'\
           '<SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="urn:vim25">' + payload + '</SOAP-ENV:Body></SOAP-ENV:Envelope>'


def convert_hostname(h):
    if opt_spaces == "cut":
        return h.split()[0]
    return h.replace(" ", "_")


def output(ln):
    vsphere_output.append(ln)


def write_output(lines, fetch_agent, address):
    if fetch_agent:
        win_agent_output = get_agent_info_tcp(address)
        sys.stdout.write(win_agent_output)

    for ln in lines:
        sys.stdout.write((ln.encode("utf-8") if isinstance(ln, unicode) else ln) + "\n")
    sys.stdout.flush()


def get_agent_info_tcp(address):
    raw_response = ""
    try:
        # TODO: gethostbyname() automatically detects IP addresses and does
        # *not* contact any nameserver in that case. So the following two
        # lines of code should not be neccessary:
        if address[0] in "123456789":
            ipaddress = address
        else:
            ipaddress = socket.gethostbyname(address)
        soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            soc.settimeout(opt_timeout)
        except AttributeError:
            pass  # some old Python versions lack settimeout(). Better ignore than fail
        soc.connect((ipaddress, 6556))
        try:
            soc.setblocking(1)
        except AttributeError:
            pass
        raw_response = ""
        while True:
            received = soc.recv(4096, socket.MSG_WAITALL)
            if received:
                raw_response += received
            else:
                break
        soc.close()
        return raw_response
    except Exception:
        if opt_debug:
            raise
    return raw_response


# Create a search pattern for the get_pattern function
# create_search_pattern(["label", "summary", "startConnected", "allowGuestControl", "connected", "status"])
def create_search_pattern(tokens):
    pattern_elements = []
    for token in tokens:
        pattern_elements.append("<%(token)s>(.*?)</%(token)s>" % {"token": token})
    return ".*?".join(pattern_elements)


def get_pattern(pattern, line):
    return re.findall(pattern, line, re.DOTALL) if line else []


def encode_url(text):
    for char, replacement in [("&", "&amp;"), (">", "&gt;"), ("<", "&lt;"), ("'", "&apos;"),
                              ("\"", "&quot;")]:
        text = text.replace(char, replacement)
    return text


def output_aggregated_snapshots(vms, hostsystems=None):

    aggregated = {}
    for data in vms.itervalues():
        if hostsystems is not None:
            running_on = hostsystems.get(data.get("runtime.host"), data.get("runtime.host"))
        else:
            running_on = ''
        snapshots = data.get("snapshot.rootSnapshotList")
        if snapshots is not None:
            aggregated.setdefault(running_on, []).append(snapshots)

    for piggytarget, sn_list in aggregated.iteritems():
        output('<<<<%s>>>>' % piggytarget)
        output('<<<esx_vsphere_vm>>>')
        output('snapshot.rootSnapshotList %s' % '|'.join(sn_list))
        output('<<<<>>>>')


def section_systemtime(connection):
    try:
        response = connection.query_server(telegram_list["systemtime"])
        elements = get_pattern('<returnval>(.*)</returnval>', response)
        if elements:
            naive = datetime.datetime.strptime(elements[0], "%Y-%m-%dT%H:%M:%S.%fZ")
            utc = naive.replace(tzinfo=tz.tzutc())
            localtime = utc.astimezone(tz.tzlocal())
            output("<<<systemtime>>>")
            output(localtime.strftime("%s"))
    except Exception:
        if opt_debug:
            raise


def find_host(search_hostname, hostsystems_properties):
    for vm_host_name, attributes in hostsystems_properties.items():
        if attributes.get("name")[0] == search_hostname:
            return vm_host_name
    return None


def is_placeholder_vm(devices):
    elements = get_pattern("<VirtualDevice xsi:type=\"([^\"]+)", devices)
    if "VirtualDisk" not in elements:
        return True
    return False


def eval_virtual_device(info, _datastores):
    response = []
    virtual_devices = get_pattern("<VirtualDevice (.*?)</VirtualDevice>", info)
    show_fields = ["label", "summary", "startConnected", "allowGuestControl", "connected", "status"]
    search_pattern = create_search_pattern(show_fields)
    for virtual_device in virtual_devices:
        type_info = get_pattern("type=\"(.*?)\"", virtual_device)
        device_info = get_pattern(search_pattern, virtual_device)
        if device_info:
            response.append(
                "virtualDeviceType %s|%s" %
                (type_info[0], "|".join("%s %s" % x for x in zip(show_fields, device_info[0]))))

    return "@@".join(response)


def eval_snapshot_list(info, _datastores):
    response = []
    snapshot_info = get_pattern(
        "<name>(.*?)</name>.*?<id>(.*?)</id><createTime>(.*?)</createTime><state>(.*?)</state>",
        info)
    for entry in snapshot_info:
        try:
            # 2013-11-06T15:39:39.347543Z
            creation_time = int(time.mktime(time.strptime(entry[2][:19], "%Y-%m-%dT%H:%M:%S")))
        except ValueError:
            creation_time = 0
        response.append("%s %s %s %s" %
                        (entry[1], creation_time, entry[3], entry[0].replace("|", " ")))
    return "|".join(response)


def eval_datastores(info, datastores):
    datastore_urls = get_pattern("<name>(.*?)</name><url>(.*?)</url>", info)
    response = []
    for name, _url in datastore_urls:
        for datastore in datastores.values():
            if name == datastore["name"]:
                vm_datastore = []
                for key, value in datastore.items():
                    if key != "name":
                        key = key.split(".")[1]
                    vm_datastore.append("%s %s" % (key, value))
                response.append("|".join(vm_datastore))
                break
        else:
            # No matching datastore was found. At least add the name
            response.append("name %s" % name)
    return "@@".join(response)


def fetch_host_systems(connection):
    hostsystems_response = connection.query_server(telegram_list["hostsystems"])
    elements = get_pattern(
        '<obj type="HostSystem">(.*?)</obj>.*?<val xsi:type="xsd:string">(.*?)</val>',
        hostsystems_response)

    # On some ESX systems the cookie login does not work as expected, when the agent_vsphere
    # is called only once or twice a day. The cookie is somehow outdated, but there is no
    # authentification failed message. Instead, the query simply returns an empty data set..
    # We try to detect this here (there is always a hostsystem) and raise a MKQueryServerException
    # which forces a new login
    if not elements:
        raise MKQueryServerException("Login cookie is no longer valid")

    return dict(elements)


def fetch_datastores(connection):
    datastores_response = connection.query_server(telegram_list["datastores"])
    elements = get_pattern('<objects><obj type="Datastore">(.*?)</obj>(.*?)</objects>',
                           datastores_response)
    datastores = {}
    for datastore, content in elements:
        entries = get_pattern('<name>(.*?)</name><val xsi:type.*?>(.*?)</val>', content)
        datastores[datastore] = {}
        for name, value in entries:
            datastores[datastore][name] = value
    return datastores


def eval_sensor_info(_hostname, _current_propname, sensor_propset):
    sensor_pattern = ""
    for key in [
            "name", "label", "summary", "key", "currentReading", "unitModifier", "baseUnits",
            "sensorType"
    ]:
        sensor_pattern += "<%(name)s>(.*?)</%(name)s>.*?" % {"name": key}

    sensor_data = get_pattern(sensor_pattern, sensor_propset)
    properties, sensors = {}, {}
    fields = ("name", "label", "summary", "key", "currentReading", "unitModifier", "baseUnits",
              "sensorType")
    sensors = {row[0]: dict(zip(fields, row)) for row in sensor_data}

    return properties, sensors


def eval_hardwarestatus_info(_hostname, _current_propname, sensor_propset):
    sensor_pattern = ""
    for key in ["name", "label", "summary", "key"]:
        sensor_pattern += "<%(name)s>(.*?)</%(name)s>.*?" % {"name": key}

    sensor_data = get_pattern(sensor_pattern, sensor_propset)
    properties, sensors = {}, {}
    for name, label, summary, key in sensor_data:
        sensors[name] = {"name": name, "label": label, "summary": summary, "key": key}
    return properties, sensors


def eval_multipath_info(_hostname, current_propname, multipath_propset):
    multipath_infos = get_pattern("<id>(.*?)</id>.*?((?:<path>.*?</path>)+)", multipath_propset)
    properties, sensors = {}, {}
    for vml_id, xml_paths in multipath_infos:
        # The Lun ID is part of the VML ID: https://kb.vmware.com/s/article/2078730
        lun_id = vml_id[10:-12]
        for path_name, path_state in get_pattern("<name>(.*?)</name>.*?<state>(.*?)</state>",
                                                 xml_paths):
            properties.setdefault(current_propname,
                                  []).append("%s %s %s" % (lun_id, path_name, path_state))
    return properties, sensors


def eval_propset_block(_hostname, current_propname, elements, id_key, propset):
    pattern = ""
    for key in elements:
        pattern += "<%(name)s>(.*?)</%(name)s>.*?" % {"name": key}

    data = get_pattern(pattern, propset)
    properties, sensors = {}, {}
    for match_groups in data:
        entries = dict(zip(elements, match_groups))
        for key, value in entries.items():
            properties.setdefault("%s.%s.%s" % (current_propname, key, entries[id_key]),
                                  []).append(value)
    return properties, sensors


def eval_cpu_pkg(hostname, current_propname, cpu_pkg_propset):
    return eval_propset_block(hostname, current_propname,
                              ["index", "vendor", "hz", "busHz", "description"], "index",
                              cpu_pkg_propset)


def eval_pci_device(hostname, current_propname, pci_propset):
    return eval_propset_block(hostname, current_propname, ["id", "vendorName", "deviceName"], "id",
                              pci_propset)


def eval_systeminfo_other(_hostname, _current_propname, otherinfo_propset):
    data = get_pattern("<identifierValue>(.*?)</identifierValue>.*?<key>(.*?)</key>",
                       otherinfo_propset)
    keys_index = {}

    properties, sensors = {}, {}
    for value, key in data:
        idx = 0
        if key in keys_index:
            keys_index[key] = keys_index[key] + 1
            idx = keys_index[key]
        properties["hardware.systemInfo.otherIdentifyingInfo.%s.%d" % (key, idx)] = [value]
        keys_index[key] = idx
    return properties, sensors


def output_datastores(datastores):
    output("<<<esx_vsphere_datastores:sep(9)>>>")
    for key in sorted(datastores.keys()):
        data = datastores[key]
        output("[%s]" % data.get("name"))
        for ds_key in sorted(data.keys()):
            if ds_key == "name":
                continue
            output("%s\t%s" % (ds_key.split(".")[1], data[ds_key]))


def fetch_hostsystem_data(connection):
    esxhostdetails_response = connection.query_server(telegram_list["esxhostdetails"])
    hostsystems_objects = get_pattern('<objects>(.*?)</objects>', esxhostdetails_response)

    eval_functions = {
        "config.storageDevice.multipathInfo": eval_multipath_info,
        "runtime.healthSystemRuntime.systemHealthInfo.numericSensorInfo": eval_sensor_info,
        "runtime.healthSystemRuntime.hardwareStatusInfo.storageStatusInfo": eval_hardwarestatus_info,
        "runtime.healthSystemRuntime.hardwareStatusInfo.cpuStatusInfo": eval_hardwarestatus_info,
        "runtime.healthSystemRuntime.hardwareStatusInfo.memoryStatusInfo": eval_hardwarestatus_info,
        "hardware.cpuPkg": eval_cpu_pkg,
        "hardware.pciDevice": eval_pci_device,
        "hardware.systemInfo.otherIdentifyingInfo": eval_systeminfo_other,
    }

    hostsystems_properties, hostsystems_sensors = {}, {}
    for entry in hostsystems_objects:
        hostname = get_pattern('<obj type="HostSystem">(.*)</obj>', entry[:512])[0]
        hostsystems_properties[hostname] = {}
        hostsystems_sensors[hostname] = {}

        elements = get_pattern('<propSet><name>(.*?)</name><val.*?>(.*?)</val></propSet>', entry)
        for current_propname, value in elements:
            if eval_functions.get(current_propname):
                properties, sensors = eval_functions[current_propname](hostname, current_propname,
                                                                       value)
                hostsystems_properties[hostname].update(properties)
                hostsystems_sensors[hostname].update(sensors)
            else:
                hostsystems_properties[hostname].setdefault(current_propname, []).append(value)

    return hostsystems_properties, hostsystems_sensors


def output_hostsystems(hostsystems_properties, hostsystems_sensors):
    # TODO: improve error handling: check if multiple results and opt_direct
    for hostname, properties in hostsystems_properties.items():
        if not opt_direct:
            output("<<<<%s>>>>" % convert_hostname(properties["name"][0]))

        output("<<<esx_vsphere_hostsystem>>>")
        for key in sorted(properties.keys()):
            output("%s %s" % (key, " ".join(properties[key])))

        output("<<<esx_vsphere_sensors:sep(59)>>>")
        for key in sorted(hostsystems_sensors[hostname].keys()):
            data = hostsystems_sensors[hostname][key]
            if data["key"].lower() in ["green", "unknown"]:
                continue
            output('%s;%s;%s;%s;%s;%s;%s;%s;%s' %
                   (data["name"].replace(";", "_"), data.get(
                       "baseUnits", ""), data.get("currentReading", ""), data.get("sensorType", ""),
                    data.get("unitModifier", ""), data.get("rateUnits", ""), data["key"],
                    data["label"], data["summary"].replace(";", "_")))


def output_licenses(licenses_response):
    output("<<<esx_vsphere_licenses:sep(9)>>>")
    root_node = minidom.parseString(licenses_response)
    licenses_node = root_node.getElementsByTagName("LicenseManagerLicenseInfo")
    for license_node in licenses_node:
        total = license_node.getElementsByTagName("total")[0].firstChild.data
        if total == "0":
            continue
        name = license_node.getElementsByTagName("name")[0].firstChild.data
        used = license_node.getElementsByTagName("used")[0].firstChild.data
        output("%s\t%s %s" % (name, used, total))


def fetch_virtual_machines(connection, hostsystems, datastores):
    vms, vm_esx_host = {}, {}

    # <objects><propSet><name>...</name><val ..>...</val></propSet></objects>
    vmdetails_response = connection.query_server(telegram_list["vmdetails"])

    elements = get_pattern("<objects>(.*?)</objects>", vmdetails_response)
    for entry in elements:
        vm_data = dict(get_pattern("<name>(.*?)</name><val.*?>(.*?)</val>", entry))
        if opt_skip_placeholder_vm and is_placeholder_vm(vm_data.get("config.hardware.device")):
            continue

        if "runtime.host" in vm_data:
            vm_data["runtime.host"] = hostsystems.get(vm_data["runtime.host"],
                                                      vm_data["runtime.host"])

        vm_esx_host.setdefault(vm_data["runtime.host"], []).append(vm_data["name"])

        transform_functions = {
            "snapshot.rootSnapshotList": eval_snapshot_list,
            "config.datastoreUrl": eval_datastores,
            "config.hardware.device": eval_virtual_device,
        }
        for key, transform in transform_functions.items():
            if key in vm_data:
                vm_data[key] = transform(vm_data[key], datastores)

        if opt_vm_piggyname == "hostname" and vm_data.get("summary.guest.hostName"):
            vms[vm_data.get("summary.guest.hostName")] = vm_data
        else:
            vms[vm_data.get("name")] = vm_data

    return vms, vm_esx_host


def output_virtual_machines(vms):
    for key in sorted(vms.keys()):
        data = vms[key]
        if data.get("name"):
            output("<<<<%s>>>>" % convert_hostname(key))
            output("<<<esx_vsphere_vm>>>")
            for entry in sorted(data.items()):
                output("%s %s" % entry)


def process_clusters(connection, vm_esx_host):
    if opt_direct:
        return

    response = connection.query_server(telegram_list["datacenters"])
    datacenters = get_pattern('<objects><obj type="Datacenter">(.*?)</obj>', response)
    for datacenter in datacenters:
        response = connection.query_server(telegram_list["clustersofdatacenter"],
                                           payload_params={"datacenter": datacenter})
        clusters = get_pattern(
            '<objects><obj type="ClusterComputeResource">(.*?)</obj>.*?string">(.*?)</val></propSet></objects>',
            response)

        output("<<<esx_vsphere_clusters:sep(9)>>>")
        for cluster in clusters:
            response = connection.query_server(telegram_list["esxhostsofcluster"],
                                               payload_params={"clustername": cluster[0]})
            cluster_vms = []
            hosts = get_pattern(
                '<objects><obj type="HostSystem">.*?string">(.*?)</val></propSet></objects>',
                response)
            for host in hosts:
                cluster_vms.extend(vm_esx_host.get(host, []))
            output("%s\thostsystems\t%s\t%s" % (datacenter, cluster[1], "\t".join(hosts)))
            output("%s\tvms\t%s\t%s" %
                   (datacenter, cluster[1], "\t".join(map(convert_hostname, cluster_vms))))


def fetch_data(connection):
    output("<<<esx_systeminfo>>>")
    for entry in connection.system_info.items():
        output("%s %s" % entry)

    #############################
    # Determine available host systems
    #############################
    hostsystems = fetch_host_systems(connection)

    ###########################
    # Licenses
    ###########################
    if "licenses" in query_objects:
        licenses_response = connection.query_server(telegram_list["licensesused"])
        output_licenses(licenses_response)

    ###########################
    # Datastores
    ###########################
    # We need the datastore info later on in the virtualmachines and counter sections
    datastores = fetch_datastores(connection)

    if "datastore" in query_objects:
        output_datastores(datastores)

    ###########################
    # Counters
    ###########################
    if "counters" in query_objects:
        process_counters(connection, hostsystems, datastores)

    ###########################
    # Hostsystem
    ###########################
    if "hostsystem" in query_objects:
        hostsystems_properties, hostsystems_sensors = fetch_hostsystem_data(connection)
        output_hostsystems(hostsystems_properties, hostsystems_sensors)

    ###########################
    # Virtual machines
    ###########################
    vm_esx_host = {}
    if "virtualmachine" in query_objects:
        vms, vm_esx_host = fetch_virtual_machines(connection, hostsystems, datastores)
        output_virtual_machines(vms)

        if opt_snapshot_display == 'esxhost':
            output_aggregated_snapshots(vms, hostsystems)
        elif opt_snapshot_display == 'vCenter':
            output_aggregated_snapshots(vms)

    # TODO: Where is the related piggyback open tag?
    output("<<<<>>>>")

    process_clusters(connection, vm_esx_host)

    output("<<<esx_vsphere_objects:sep(9)>>>")

    # the piggybacked data is printed later on, because it looks quite messy...
    vm_piggy_data = {}
    host_piggy_data = {}

    if "hostsystem" in query_objects:
        if opt_host_pwr_display != "vm":  # handled later on..
            if opt_direct and opt_hostname:
                for hostname, data in hostsystems_properties.items():
                    output(
                        "hostsystem\t%s\t\t%s" %
                        (opt_hostname, hostsystems_properties[hostname]["runtime.powerState"][0]))
            else:
                for hostname, data in hostsystems_properties.items():
                    converted_hostname = convert_hostname(
                        hostsystems_properties[hostname]["name"][0])
                    host_info = "hostsystem\t%s\t\t%s" % (
                        converted_hostname,
                        hostsystems_properties[hostname]["runtime.powerState"][0])
                    if opt_host_pwr_display == "esxhost" and not opt_direct:
                        host_piggy_data.setdefault(converted_hostname, []).append(host_info)
                    output(host_info)

    if "virtualmachine" in query_objects:
        for key in vms:
            data = vms[key]
            running_on = hostsystems.get(data.get("runtime.host"), data.get("runtime.host"))
            vm_info = "virtualmachine\t%s\t%s\t%s" % (convert_hostname(key), running_on,
                                                      data.get("runtime.powerState"))

            if opt_vm_pwr_display == "vm":
                vm_name = convert_hostname(key)
                vm_piggy_data.setdefault(vm_name, []).append(vm_info)
            elif opt_vm_pwr_display == "esxhost" and not opt_direct:
                host_piggy_data.setdefault(running_on, []).append(vm_info)
            output(vm_info)

    if ("virtualmachine" in query_objects and "hostsystem" in query_objects and
            opt_host_pwr_display == "vm"):

        for key in vms:
            data = vms[key]
            running_on = hostsystems.get(data.get("runtime.host"), data.get("runtime.host"))
            vm_host = find_host(running_on, hostsystems_properties)
            if not vm_host:
                continue

            vm_info = "hostsystem\t%s\t\t%s" % (
                running_on, hostsystems_properties[vm_host]["runtime.powerState"][0])
            vm_name = convert_hostname(key)
            vm_piggy_data.setdefault(vm_name, []).append(vm_info)

    for entries in [host_piggy_data, vm_piggy_data]:
        for key, values in entries.items():
            output("<<<<%s>>>>" % key)
            output("<<<esx_vsphere_objects:sep(9)>>>")
            output("\n".join(values))
    output("<<<<>>>>")

    section_systemtime(connection)


def call_legacy_pysphere():
    # TODO: Remove this, drop agent_vsphere.pysphere
    import subprocess

    path_vsphere_pysphere = os.path.dirname(os.path.abspath(__file__))
    cmd = ["%s/agent_vsphere.pysphere" % path_vsphere_pysphere] + sys.argv[1:]
    return subprocess.call(cmd)


#   .--Main----------------------------------------------------------------.
#   |                        __  __       _                                |
#   |                       |  \/  | __ _(_)_ __                           |
#   |                       | |\/| |/ _` | | '_ \                          |
#   |                       | |  | | (_| | | | | |                         |
#   |                       |_|  |_|\__,_|_|_| |_|                         |
#   |                                                                      |
#   +----------------------------------------------------------------------+

if __name__ == "__main__":
    short_options = "hi:u:s:Dat:H:Pp:S:"
    long_options = [
        "help", "user=", "secret=", "direct", "agent", "debug", "modules=", "timeout=",
        "no-cert-check", "hostname=", "tracefile=", "pysphere", "port=", "spaces=",
        "host_pwr_display=", "vm_pwr_display=", "snapshot_display=", "vm_piggyname="
    ]

    opt_debug = False
    opt_direct = False
    opt_agent = False
    opt_timeout = 60
    opt_port = 443
    opt_hostname = None
    opt_skip_placeholder_vm = False
    opt_pysphere = False
    opt_tracefile = None
    opt_host_pwr_display = None
    opt_vm_pwr_display = None
    opt_snapshot_display = None
    opt_vm_piggyname = "alias"
    opt_spaces = "underscore"
    opt_no_cert = False

    host_address = None
    g_user = None
    g_secret = None
    tracefile = None
    query_objects = ['hostsystem', 'virtualmachine', 'datastore', 'counters', 'licenses']

    try:
        opts, args = getopt.getopt(sys.argv[1:], short_options, long_options)
    except getopt.GetoptError, get_opt_exc:
        sys.stderr.write("%s\n" % get_opt_exc)
        sys.exit(1)

    for o, a in opts:
        if o in ['--debug']:
            opt_debug = True
        elif o in ['--tracefile']:
            opt_tracefile = a
        elif o in ['-D', '--direct']:
            opt_direct = True
        elif o in ['-a', '--agent']:
            opt_agent = True
        elif o == '-P':
            opt_skip_placeholder_vm = True
        elif o in ['-p', '--port']:
            opt_port = a
        elif o in ['--no-cert-check']:
            opt_no_cert = True
        elif o == '--pysphere':
            opt_pysphere = True
        elif o in ['-u', '--user']:
            g_user = a
        elif o in ['-s', '--secret']:
            g_secret = a
        elif o in ['-i', '--modules']:
            query_objects = a.split(',')
        elif o in ['-t', '--timeout']:
            opt_timeout = int(a)
        elif o in ['-H', '--hostname']:
            opt_hostname = a
        elif o in ['--vm_piggyname']:
            opt_vm_piggyname = a
        elif o in ['--vm_pwr_display']:
            opt_vm_pwr_display = a
        elif o in ['--host_pwr_display']:
            opt_host_pwr_display = a
        elif o in ['--snapshot_display']:
            opt_snapshot_display = a
        elif o in ['-S', '--spaces']:
            if a not in ["cut", "underscore"]:
                usage()
                sys.exit(1)
            opt_spaces = a
        elif o in ['-h', '--help']:
            usage()
            sys.exit(0)

    # If the --pysphere option is set we use the legacy pysphere agent, though 50 times slower...
    if opt_pysphere:
        sys.exit(call_legacy_pysphere())

    if len(args) == 1:
        host_address = args[0]
    elif not args:
        sys.stderr.write("ERROR: No host given.\n")
        sys.exit(1)
    else:
        sys.stderr.write("ERROR: Please specify exactly one host.\n")
        sys.exit(1)

    vsphere_output = []

    socket.setdefaulttimeout(opt_timeout)

    if opt_tracefile:
        tracefile_dir = os.path.dirname(opt_tracefile) or "."
        if os.path.exists(tracefile_dir):
            tracefile = file(opt_tracefile, "w")
        elif opt_debug:
            sys.stderr.write("Path for tracefile %s does not exist" % opt_tracefile)
            sys.stderr.flush()

    if tracefile:
        tracefile.write("Tracefile %s Host address: %s\n" %
                        (datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), host_address))

    try:
        esx_connection = ESXConnection((host_address, opt_port), g_user, g_secret, opt_no_cert,
                                       opt_debug)

        # If the data aquisition fails, e.g. invalid cookie we try another run
        for _run_count in [0, 1]:
            # Note: If the cookie fails inbetween these calls, we also start a second run
            try:
                esx_connection.login()
                fetch_data(esx_connection)
                break
            except MKQueryServerException:
                # There was a problem during the query.
                # It is possible that the server cookie got invalid.
                # We are delete the cookie and start a second run
                vsphere_output = []  # Reset current output
                esx_connection.delete_server_cookie()
                continue  # do a second run
            except Exception:
                if opt_debug:
                    raise
                raise MKGeneralException("Error while processing received data")

    except MKGeneralException as exc:
        sys.stderr.write("%s\n" % exc)
        sys.exit(0 if opt_agent else 1)

    write_output(vsphere_output, opt_agent, host_address)

    sys.exit(0)
