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

# 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

import httplib, pprint, sys, os, getopt, socket, time, datetime, re

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>',

       "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>',

     "licenseassigned":
         '<ns1:QueryAssignedLicenses xsi:type="ns1:QueryAssignedLicensesRequestType">'\
         '<ns1:_this type="LicenseAssignmentManager">LicenseAssignmentManager</ns1:_this></ns1:QueryAssignedLicenses>',

     "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>1</ns1:maxSample>%(counters)s'\
         '<ns1:intervalId>20</ns1:intervalId></ns1:querySpec></ns1:QueryPerf>',

     "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.multipathState.path</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.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>guest.toolsVersion</ns1:pathSet>'\
           '<ns1:pathSet>guestHeartbeatStatus</ns1:pathSet>'\
           '<ns1:pathSet>name</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>',

     "logout":
         '<ns1:Logout xsi:type="ns1:LogoutRequestType">'\
         '<ns1:_this type="SessionManager">%(sessionManager)s</ns1:_this></ns1:Logout>',

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

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]
    else:
        return h.replace(" ", "_")

def get_counters(esx_version_num):
    needed_ids = [
        # sys.uptime
        262144,
        # mem.*
        # 65635, 65537, 65577, 65625, 65545, 65611, 65573, 65615, 65582,
        # 65549, 65630, 65622, 65618, 65621, 65561, 65623, 65632, 65557,
        # 65628, 65633, 65541, 65643, 65586, 65553, 65569, 65589, 65639,
        # 65620, 65599, 65580, 65619, 65603,
        #  'disk.deviceLatency': 131091,
        #  'disk.deviceReadLatency': 131083,
        #  'disk.deviceWriteLatency': 131087,
        #  'disk.kernelLatency': 131092,
        #  'disk.kernelReadLatency': 131084,
        #  'disk.kernelWriteLatency': 131088,
        #  'disk.maxQueueDepth': 131096,
        #  'disk.maxTotalLatency': 131095,
        #  'disk.numberRead': 131076,
        #  'disk.numberReadAveraged': 131097,
        #  'disk.numberWrite': 131077,
        #  'disk.numberWriteAveraged': 131098,
        #  'disk.queueLatency': 131094,
        #  'disk.queueReadLatency': 131086,
        #  'disk.queueWriteLatency': 131090,
        #  'disk.read': 131078,
        #  'disk.totalLatency': 131093,
        #  'disk.totalReadLatency': 131085,
        #  'disk.totalWriteLatency': 131089,
        #  'disk.usage': 131073,

        # disk.read/write/deviceLatency/numberRead/numberWrite
        131078, 131079, 131091, 131076, 131077,
    ]


    #needed_ids = [
    #    # sys.resourceMem*
    #    262151, 262155, 262169, 262152, 262154, 262153, 262157, 262156, 262168,
    #]

    # Seems as this is not available in ESX 5.0 but we saw it on 5.1
    if esx_version_num > 5.0:
        # sys.resourceMemConsumed
        needed_ids += [
            262171,
        ]

    if esx_version_num > 4.1:
        needed_ids += [
            # net.*
            196616, 196621, 196617, 196625, 196619, 196623, 196609, 196614,
            196620, 196624, 196615, 196622, 196618, 196612, 196613, 196626,
        ]

    return needed_ids

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.

  -c, --dump-counters           Simply dumping out all available counters and their values.
                                This mode is meant for debugging and diagnostic purposes.

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

  --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

""")

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="
]

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_tracefile           = None
opt_spaces              = "underscore"
opt_no_cert             = False

error = None
error_exit = 1

host_address  = None
user          = None
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, err:
    sys.stderr.write("%s\n" % err)
    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' ]:
        user = a
    elif o in [ '-s', '--secret' ]:
        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_pwr_display' ]:
        opt_vm_pwr_display = a
    elif o in [ '--host_pwr_display' ]:
        opt_host_pwr_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:
    path_vsphere_pysphere = os.path.dirname(os.path.abspath(__file__))
    cmd = [ "%s/agent_vsphere.pysphere" % path_vsphere_pysphere ]
    import subprocess
    p = subprocess.Popen(cmd + sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    out, err = p.communicate()
    if out:
        sys.stdout.write(out)
    if err:
        sys.stderr.write(err)
    sys.exit(p.returncode)

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)

socket.setdefaulttimeout(opt_timeout)

def get_agent_info_tcp(hostname):
    output = ""
    try:
        if hostname[0] in "123456789":
            ipaddress = hostname
        else:
            ipaddress = socket.gethostbyname(hostname)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.settimeout(opt_timeout)
        except:
            pass # some old Python versions lack settimeout(). Better ignore than fail
        s.connect((ipaddress, 6556))
        try:
            s.setblocking(1)
        except:
            pass
        output = ""
        while True:
            out = s.recv(4096, socket.MSG_WAITALL)
            if out and len(out) > 0:
                output += out
            else:
                break
        s.close()
        return output
    except Exception, e:
        if opt_debug:
            raise
    return output


# Globals of ESX System. These settings are available after the first "systeminfo" query
systemfields = [
 ("apiVersion", float),
 ("name", None),
 ("rootFolder", None),
 ("perfManager", None),
 ("sessionManager", None),
 ("licenseManager", None),
 ("propertyCollector", None)
]

systeminfo = {}

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()

def get_pattern(pattern, line):
    if not line:
        return []
    p = re.compile(pattern)
    return p.findall(line)

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

my_cookie     = None
server_handle = None
def query_server(payload, payload_params = {}):
    # Finalize payload
    payload_params.update(systeminfo)
    soapdata = put_in_envelope(payload)
    soapdata = soapdata % payload_params

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

    response_data = []

    time_sent = time.time()
    server_handle.send(soapdata)
    response  = server_handle.getresponse()
    response_data.append(response.read())

    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:
            soapdata = put_in_envelope(telegram_list["continuetoken"] % {"token": token[0]})
            init_headers(soapdata)
            server_handle.send(soapdata)
            response  = server_handle.getresponse()
            response_data.append(response.read())
        else:
            break
    time_response = time.time()

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

    return response.status, response.reason, response.msg, "".join(response_data)

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

# Initialize server connection
try:
    netloc        = host_address + ":" + str(opt_port)

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

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

    server_handle.connect()


    # Retrieve basic data, like systeminfo and session cookie
    for name in [ "systeminfo", "login" ]:
        payload = telegram_list[name]

        if name == "login":
            reply_code, repy_msg, reply_headers, reply_data = query_server(payload, payload_params = { "username": encode_url(user),
                                                                                       "password": encode_url(secret)})
        else:
            reply_code, reply_msg, reply_headers, reply_data = query_server(payload)

        if name == "systeminfo":
            for entry, function in systemfields:
                 element = get_pattern("<%(entry)s.*>(.*)</%(entry)s>" % { "entry": entry }, reply_data)
                 systeminfo[entry] = function and function(element[0]) or element[0]
        elif name == "login":
            my_cookie = reply_headers.get("Set-Cookie")
            if "InvalidLogin" in reply_data:
                raise Exception("Login response is not 'OK'. Please check the credentials")


except Exception, e:
    if opt_debug:
        raise
    error = "Cannot connect to vSphere Server. Maybe you provided wrong " \
            "credentials. Please check your connection settings and try " \
            "again. Error message: %r" % e


if not error:
    try:

        ##########################
        # Check MK header and Agent Version
        print   "<<<check_mk>>>"
        print "Version: %.1f" % systeminfo["apiVersion"]
        print "AgentOS: %s" %   systeminfo["name"]

        ###########################
        # Licenses
        ###########################
        if "licenses" in query_objects:
            reply_code, reply_msg, reply_headers, licenses_response = query_server(telegram_list["licenseassigned"])
            license_hosts = get_pattern('<returnval>(.*?)</returnval>', licenses_response)
            licenses = {}
            for host in license_hosts:
                license_info = get_pattern("<licenseKey>(.*?)</licenseKey>.*?<name>(.*?)</name><total>(.*?)</total><used>(.*?)</used>", host)
                if not license_info:
                    continue
                key, name, total, used  = license_info[0]
                # We merge all entries with the same name, duplicate keys are ignored since they provide the same data
                licenses.setdefault(name, {"used_keys": [], "total": 0, "used": 0})
                if key not in licenses[name]["used_keys"]:
                    licenses[name]["total"] += int(total)
                    licenses[name]["used"]  += int(used)
                    licenses[name]["used_keys"].append(key)

            print "<<<esx_vsphere_licenses:sep(9)>>>"
            for license, values in licenses.items():
                print "%s\t%s %s" % (license, values["used"], values["total"])

        #############################
        # Determine available host systems
        #############################
        hostsystems = {}

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

        for hostsystem, name in elements:
            hostsystems[hostsystem] = name


        ###########################
        # Datastores
        ###########################
        datastores = {} # We also need the datastore info later in the virtualmachines section
        if "datastore" in query_objects:
            reply_code, reply_msg, reply_headers, datastores_response = query_server(telegram_list["datastores"])
            elements = get_pattern('<objects><obj type="Datastore">(.*?)</obj>(.*?)</objects>', datastores_response)
            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

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

        ###########################
        # Counters
        ###########################
        if "counters" in query_objects:
            # Get counter syntax
            counter_syntax_payload = telegram_list["perfcountersyntax"]
            needed_ids             = get_counters(systeminfo["apiVersion"])
            counter_data = []
            for entry in needed_ids:
                counter_data.append("<ns1:counterId>%s</ns1:counterId>" % entry)

            reply_code, reply_msg, reply_headers, counters_syntax_response = \
                query_server(counter_syntax_payload, payload_params = { "counters": "".join(counter_data)})

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

            for key, nameInfo, groupInfo, unitInfo in elements:
                counters_syntax[key] = { "name": nameInfo, "group": groupInfo, "unit": unitInfo }

            # We need to query each ESX host system separately..
            hostnames = hostsystems.keys()
            counter_data_payload = telegram_list["perfcounterdata"]
            for idx, host in enumerate(hostnames):
                if not opt_direct:
                    print "<<<<%s>>>>" % convert_hostname(hostsystems[host])

                ### TODO: check if this host supports perfdata
                ### TODO: try to get esx hosts perfdata from vCenter
                counter_avail = telegram_list["perfcounteravail"]
                reply_code, reply_msg, reply_headers, counter_avail_response = query_server(counter_avail, payload_params = { "esxhost": host })

                counters_avail  = {}
                p = re.compile("<counterId>([0-9]*)</counterId><instance>([^<]*)")
                elements = get_pattern("<counterId>([0-9]*)</counterId><instance>([^<]*)", counter_avail_response)
                for counter, instance in elements:
                    counters_avail.setdefault(counter, []).append(instance)

                counter_data = []
                for entry in needed_ids:
                    for instance in counters_avail.get(entry, []):
                        counter_data.append("<ns1:metricId><ns1:counterId>%d</ns1:counterId><ns1:instance>%s</ns1:instance></ns1:metricId>" % (entry, instance))
                counters_value = {}
                reply_code, reply_msg, reply_headers, counter_data_response = \
                    query_server(counter_data_payload, payload_params = { "esxhost": host, "counters": "".join(counter_data) })
                elements = get_pattern("<id><counterId>(.*?)</counterId><instance>(.*?)</instance></id><value>(.*?)</value>", counter_data_response)

                for counter, instance, value in elements:
                    if counter in counters_syntax:
                        counters_value["%s.%s" % (counter, instance)] = { "id": counter, "instance": instance, "value": value }

                print "<<<esx_vsphere_counters:sep(124)>>>"
                counters_output = {}
                for key in sorted(counters_value.keys()):
                    value = counters_value[key]
                    desc  = counters_syntax[value["id"]]
                    counters_output[ (desc["group"], desc["name"], value["instance"]) ] = ( value["value"], desc["unit"] )

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

            if not opt_direct:
                print '<<<<>>>>'

        ###########################
        # Hostsystem
        ###########################
        if "hostsystem" in query_objects:
            hostsystems_properties = {}
            hostsystems_sensors    = {}

            # Propsets
            reply_code, reply_msg, reply_headers, esxhostdetails_response = query_server(telegram_list["esxhostdetails"])
            hostsystems_objects = get_pattern('<objects>(.*?)</objects>', esxhostdetails_response)

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

                current_propname = ""

                def eval_sensor_info(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)
                    for name, label, summary, key, currentReading, unitModifier, baseUnits, sensorType in sensor_data:
                        hostsystems_sensors[hostname][name] = { "name": name, "label": label, "summary": summary, "key": key,
                                                                "currentReading": currentReading, "unitModifier": unitModifier,
                                                                "baseUnits": baseUnits, "sensorType": sensorType }

                def eval_multipath_state(multipath_propset):
                    multipaths = get_pattern("<name>(.*?)</name><pathState>(.*?)</pathState>", value)
                    for mp_name, mp_state in multipaths:
                        hostsystems_properties[hostname].setdefault(current_propname, []).append("%s %s" % (mp_name, mp_state))

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

                    data = get_pattern(pattern, propset)
                    for match_groups in data:
                        entries = dict(zip(elements, match_groups))
                        for key, value in entries.items():
                            hostsystems_properties[hostname].setdefault("%s.%s.%s" % \
                                (current_propname, key, entries[id_key]), []).append(value)

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

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

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

                    for value, key in data:
                        idx = 0
                        if key in keys_index:
                            keys_index[key] = keys_index[key] + 1
                            idx = keys_index[key]
                        hostsystems_properties[hostname]["hardware.systemInfo.otherIdentifyingInfo.%s.%d" % (key, idx)] = [ value ]
                        keys_index[key] = idx

                eval_functions = {
                    "config.multipathState.path"                                    : eval_multipath_state,
                    "runtime.healthSystemRuntime.systemHealthInfo.numericSensorInfo": eval_sensor_info,
                    "hardware.cpuPkg"                                               : eval_cpu_pkg,
                    "hardware.pciDevice"                                            : eval_pci_device,
                    "hardware.systemInfo.otherIdentifyingInfo"                      : eval_systeminfo_other,
                }

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

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

                print "<<<esx_vsphere_hostsystem>>>"
                for key in sorted(properties.keys()):
                    if key == "runtime.healthSystemRuntime.systemHealthInfo.numericSensorInfo":
                        continue
                    value = properties[key]
                    print "%s %s" % (key, " ".join(value))

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


        ###########################
        # Virtual machines
        ###########################
        if "virtualmachine" in query_objects:
            vms = {}

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

            # <objects><propSet><name>...</name><val ..>...</val></propSet></objects>
            reply_code, reply_msg, reply_headers, vmdetails_response = 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
                else:
                    # we don't need this in the agent output
                    if vm_data.get("config.hardware.device"):
                        del vm_data["config.hardware.device"]
                if "runtime.host" in vm_data:
                    vm_data["runtime.host"] = hostsystems.get(vm_data["runtime.host"], vm_data["runtime.host"])

                def eval_snapshot_list(info):
                    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:
                            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):
                    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 "\t".join(response)

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

                vms[vm_data.get("name")] = vm_data


            for key in sorted(vms.keys()):
                data = vms[key]
                if data.get("name"):
                    print "<<<<%s>>>>" % convert_hostname(data.get("name"))
                    print "<<<esx_vsphere_vm>>>"
                    entries = list(data.items())
                    entries.sort()
                    for entry in entries:
                        print "%s %s" % entry


        print "<<<<>>>>"

        print "<<<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():
                        print "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)
                        else:
                            print host_info

        if "virtualmachine" in query_objects:
            for key in vms.keys():
                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(data.get("name")), running_on, data.get("runtime.powerState"))

                if opt_vm_pwr_display == "vm":
                    vm_name = convert_hostname(data.get("name"))
                    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)
                else:
                    print vm_info

        if "virtualmachine" in query_objects and "hostsystem" in query_objects and opt_host_pwr_display == "vm":
            def find_host(hostname):
                for key, value in hostsystems_properties.items():
                    if value.get("name")[0] == hostname:
                        return key

            for key in vms.keys():
                data = vms[key]
                running_on = hostsystems.get(data.get("runtime.host"), data.get("runtime.host"))
                vm_host = find_host(running_on)
                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(data.get("name"))
                vm_piggy_data.setdefault(vm_name, []).append(vm_info)

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

        query_server(telegram_list["logout"])

    except Exception, e:
        if opt_debug:
            raise
        error = "Error while processing received data"

if opt_agent:
    sys.stdout.write(get_agent_info_tcp(host_address))
    sys.stdout.flush()
    error_exit = 0

if tracefile:
    tracefile.close()

if error:
    sys.stderr.write(error + "\n")
    sys.exit(error_exit)
