diff --git a/lib/check/host_vms.py b/lib/check/host_vms.py index e0e0b28..6d0fda3 100644 --- a/lib/check/host_vms.py +++ b/lib/check/host_vms.py @@ -1,183 +1,7 @@ import logging -import os from libprobe.asset import Asset from pyVmomi import vim # type: ignore -from ..utils import datetime_to_timestamp -from ..vmwarequery import vmwarequery, vmwarequery_perf - - -COLLECT_PERFORMANCE_METRICS = int(os.getenv('COLLECT_PERFORMANCE_METRICS', '1')) - - -def on_guest_info(obj): - # vim.vm.GuestInfo - return { - 'appHeartbeatStatus': obj.appHeartbeatStatus, # str - 'appState': obj.appState, # str/null - 'guestFamily': obj.guestFamily, # str - 'guestFullName': obj.guestFullName, # str - 'guestId': obj.guestId, # str - 'guestKernelCrashed': obj.guestKernelCrashed, # bool/null - 'guestOperationsReady': obj.guestOperationsReady, # bool - 'guestState': obj.guestState, # str - 'guestStateChangeSupported': obj.guestStateChangeSupported, # bool - 'hostName': obj.hostName, # str - 'interactiveGuestOperationsReady': - obj.interactiveGuestOperationsReady, # bool - 'ipAddress': obj.ipAddress, # str - 'toolsInstallType': obj.toolsInstallType, # str/null - 'toolsRunningStatus': obj.toolsRunningStatus, # str - 'toolsStatus': obj.toolsStatus, # str - 'toolsVersion': obj.toolsVersion, # str - 'toolsVersionStatus': obj.toolsVersionStatus, # str - 'toolsVersionStatus2': obj.toolsVersionStatus2, # str - } - - -def on_runtime_info(obj): - # vim.vm.RuntimeInfo - return { - 'bootTime': datetime_to_timestamp(obj.bootTime), # int/null - 'cleanPowerOff': obj.cleanPowerOff, # bool - 'connectionState': obj.connectionState, # str - 'consolidationNeeded': obj.consolidationNeeded, # str - 'cryptoState': obj.cryptoState, # str/null - 'faultToleranceState': obj.faultToleranceState, # str - 'instantCloneFrozen': obj.instantCloneFrozen, # bool/null - 'maxCpuUsage': obj.maxCpuUsage, # int/null - 'maxMemoryUsage': obj.maxMemoryUsage, # int - 'memoryOverhead': obj.memoryOverhead, # int - 'minRequiredEVCModeKey': obj.minRequiredEVCModeKey, # str - 'needSecondaryReason': obj.needSecondaryReason, # str/null - 'numMksConnections': obj.numMksConnections, # int - 'onlineStandby': obj.onlineStandby, # bool - 'paused': obj.paused, # bool - 'powerState': obj.powerState, # str - 'quiescedForkParent': obj.quiescedForkParent, # bool/null - 'recordReplayState': obj.recordReplayState, # str - 'snapshotInBackground': obj.snapshotInBackground, # bool - 'suspendInterval': obj.suspendInterval, # int - 'suspendTime': datetime_to_timestamp(obj.suspendTime), - 'toolsInstallerMounted': obj.toolsInstallerMounted, # bool - 'vFlashCacheAllocation': obj.vFlashCacheAllocation, # int - } - - -def on_virtual_hardware(obj): - # vim.vm.VirtualHardware - return { - 'memoryMB': obj.memoryMB, - 'numCPU': obj.numCPU, - 'numCoresPerSocket': obj.numCoresPerSocket, - 'virtualICH7MPresent': obj.virtualICH7MPresent, - 'virtualSMCPresent': obj.virtualSMCPresent, - } - - -def on_config_info(obj): - # vim.vm.ConfigInfo - return { - **on_virtual_hardware(obj.hardware), - 'alternateGuestName': obj.alternateGuestName, # str - 'annotation': obj.annotation, # str - 'changeTrackingEnabled': obj.changeTrackingEnabled, # bool - 'changeVersion': obj.changeVersion, # str - 'createDate': datetime_to_timestamp(obj.createDate), # int/null - 'cpuHotAddEnabled': obj.cpuHotAddEnabled, # bool - 'cpuHotRemoveEnabled': obj.cpuHotRemoveEnabled, # bool - 'firmware': obj.firmware, # str - 'guestAutoLockEnabled': obj.guestAutoLockEnabled, # bool - 'guestFullNameConfig': obj.guestFullName, # str - 'guestIdConfig': obj.guestId, # str - 'hotPlugMemoryIncrementSize': obj.hotPlugMemoryIncrementSize, # str - 'hotPlugMemoryLimit': obj.hotPlugMemoryLimit, # str - 'instanceUuid': obj.instanceUuid, # str - 'locationId': obj.locationId, # str - 'maxMksConnections': obj.maxMksConnections, # int - 'memoryHotAddEnabled': obj.memoryHotAddEnabled, # bool - 'memoryReservationLockedToMax': - obj.memoryReservationLockedToMax, # bool - 'messageBusTunnelEnabled': obj.messageBusTunnelEnabled, # bool - 'migrateEncryption': obj.migrateEncryption, # str - 'modified': datetime_to_timestamp(obj.modified), - # 'name': obj.name, # str - 'nestedHVEnabled': obj.nestedHVEnabled, # bool - 'npivDesiredNodeWwns': obj.npivDesiredNodeWwns, # int/null - 'npivDesiredPortWwns': obj.npivDesiredPortWwns, # int/null - 'migrateEncryption': obj.migrateEncryption, # str - 'npivOnNonRdmDisks': obj.npivOnNonRdmDisks, # bool - 'npivTemporaryDisabled': obj.npivTemporaryDisabled, # bool - 'npivWorldWideNameType': obj.npivWorldWideNameType, # str - 'swapPlacement': obj.swapPlacement, # str - 'swapStorageObjectId': obj.swapStorageObjectId, # str - 'template': obj.template, # bool - 'uuid': obj.uuid, # str - 'vAssertsEnabled': obj.vAssertsEnabled, # bool - 'vFlashCacheReservation': obj.vFlashCacheReservation, # int - 'vPMCEnabled': obj.vPMCEnabled, # bool - 'version': obj.version, # str - 'vmStorageObjectId': obj.vmStorageObjectId, # str - } - - -def on_virtual_disk_backing_info(obj): - # vim.vm.device.VirtualDisk.FlatVer2BackingInfo - return { - 'changeId': obj.changeId, # str/null - 'contentId': obj.contentId, # str/null - 'deltaDiskFormat': obj.deltaDiskFormat, # str/null - 'deltaDiskFormatVariant': obj.deltaDiskFormatVariant, # str/null - 'deltaGrainSize': obj.deltaGrainSize, # int/null - 'digestEnabled': obj.digestEnabled, # bool - 'diskMode': obj.diskMode, # str - 'eagerlyScrub': obj.eagerlyScrub, # bool/null - 'fileName': obj.fileName, # str - 'sharing': obj.sharing, # str - 'split': obj.split, # bool - 'thinProvisioned': obj.thinProvisioned, # bool - 'uuid': obj.uuid, # str - 'writeThrough': obj.writeThrough, # bool - } - - -def on_virtual_disk(obj): - # vim.vm.device.VirtualDisk - return { - **on_virtual_disk_backing_info(obj.backing), - 'capacityInBytes': obj.capacityInBytes, # int - 'diskObjectId': obj.diskObjectId, # str/null - 'nativeUnmanagedLinkedClone': - obj.nativeUnmanagedLinkedClone, # bool/null - } - - -def on_snapshot_tree(obj): - # vim.vm.SnapshotTree - return { - 'backupManifest': obj.backupManifest, # str - 'createTime': datetime_to_timestamp(obj.createTime), - 'description': obj.description, # str - 'id': obj.id, # int - # 'name': obj.name, # str - 'quiesced': obj.quiesced, # xsd:boolean - 'replaySupported': obj.replaySupported, # xsd:boolean - 'state': obj.state, # str - } - - -def snapshot_flat(snapshots, vm_name): - for snapshot in snapshots: - snapshot_dct = on_snapshot_tree(snapshot) - snapshot_dct['name'] = f'{vm_name}/{snapshot.id}' - snapshot_dct['snapshotName'] = snapshot.name - snapshot_dct['snapshotId'] = snapshot.id - snapshot_dct['vm'] = vm_name - yield snapshot_dct - for item in snapshot_flat( - snapshot.childSnapshotList, vm_name): - item['parentSnapshotName'] = snapshot.name - item['parentSnapshotId'] = snapshot.id - yield item +from ..vmwarequery import vmwarequery async def check_host_vms( @@ -190,15 +14,7 @@ async def check_host_vms( asset_config, check_config, vim.Datastore, - ['name', 'summary.capacity', 'info'], - ) - - hypervisors_ = await vmwarequery( - asset, - asset_config, - check_config, - vim.HostSystem, - ['name'], + ['name', 'summary.capacity'], ) vms_ = await vmwarequery( @@ -206,32 +22,17 @@ async def check_host_vms( asset_config, check_config, vim.VirtualMachine, - ['name', 'config', 'guest', 'snapshot', 'runtime'], + ['name', 'config', 'guest', 'runtime'], ) - if COLLECT_PERFORMANCE_METRICS: - vms_perf = await vmwarequery_perf( - asset, - asset_config, - check_config, - vim.VirtualMachine, - [('cpu', 'ready'), ('disk', 'busResets')], - ) - else: - vms_perf = {} - stores_lookup = { store.obj: {p.name: p.val for p in store.propSet} for store in stores_} - hypervisors_lookup = { - hyp.obj: {p.name: p.val for p in hyp.propSet} for hyp in hypervisors_} vms_retrieved = { vm.obj: {p.name: p.val for p in vm.propSet} for vm in vms_} guests = [] - virtual_disks = [] - snapshots = [] - virtual_storage_capacities = {} - + running_guest_count = 0 + virtual_storage_dct = {} for moref, vm in vms_retrieved.items(): if 'config' not in vm: logging.info( @@ -241,76 +42,40 @@ async def check_host_vms( if vm['config'].template: continue - # CHECK HOST VMS - info_dct = on_guest_info(vm['guest']) - info_dct.update(on_config_info(vm['config'])) - info_dct.update(on_runtime_info(vm['runtime'])) - info_dct['name'] = instanceuuid = vm['config'].instanceUuid - info_dct['instanceName'] = vm['name'] - - # aggregate performance metrics per guest - perf = vms_perf.get(instanceuuid) - if perf is not None: - path = ('cpu', 'ready') - total_name = '' - values = perf[path].get(total_name) - if values: - info_dct['cpuReadiness'] = max(values) / 20_000 * 100 - # number of disk bus reset commands by the virtual machine - path = ('disk', 'busResets') - info_dct['busResets'] = sum( - sum(values) - for values in perf[path].values() - ) + guests.append({ + 'name': vm['config'].instanceUuid, + 'instanceName': vm['name'], + 'powerState': vm['runtime'].powerState, + 'currentHypervisor': + # vm.runtime.host is empty when vm is off + vm['runtime'].host and vm['runtime'].host.name, + }) - hyp = hypervisors_lookup.get(vm['runtime'].host) - if hyp: - info_dct['currentHypervisor'] = hyp['name'] - guests.append(info_dct) - - # SNAPSHOTS - if 'snapshot' in vm: - snapshots.extend( - snapshot_flat( - vm['snapshot'].rootSnapshotList, vm['name'])) + if vm['guest'].guestState == 'running': + running_guest_count += 1 for device in vm['config'].hardware.device: - if (device.key >= 2000) and (device.key < 3000): - # DISKS - disk_dct = on_virtual_disk(device) - disk_dct['name'] = device.backing.fileName - + if isinstance(device, vim.vm.device.VirtualDisk): + datastore_name = device.backing.datastore.name datastore = stores_lookup[device.backing.datastore] - datastore_name = datastore['name'] - disk_dct['datastore'] = datastore['name'] - if hasattr(device, 'deviceInfo') and device.deviceInfo: - disk_dct['label'] = device.deviceInfo.label - if disk_dct['capacityInBytes'] and \ - disk_dct['datastore']: - if datastore_name not in \ - virtual_storage_capacities: - virtual_storage_capacities[datastore_name] = { - 'virtualCapacity': 0, - 'name': datastore_name, - 'actualCapacity': datastore[ - 'summary.capacity']} - virtual_storage_capacities[datastore_name][ - 'virtualCapacity'] += disk_dct[ - 'capacityInBytes'] - virtual_disks.append(disk_dct) + if datastore_name not in virtual_storage_dct: + virtual_storage_dct[datastore_name] = { + 'name': datastore_name, + 'actualCapacity': datastore['summary.capacity'], + 'virtualCapacity': 0 + } + virtual_storage_dct[datastore_name]['virtualCapacity'] += \ + device.capacityInBytes guest_count = [{ 'name': 'guestCount', 'guestCount': len(guests), - 'runningGuestCount': sum( - guest.get('guestState') == 'running' - for guest in guests) + 'runningGuestCount': running_guest_count }] + virtual_storage = list(virtual_storage_dct.values()) return { 'guests': guests, 'guestCount': guest_count, - 'virtualDisks': virtual_disks, - 'snapshots': snapshots, - 'virtualStorage': list(virtual_storage_capacities.values()) + 'virtualStorage': virtual_storage, } diff --git a/lib/version.py b/lib/version.py index b039cba..426a70e 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,4 +1,4 @@ # Version string. Examples: # '3.0.0' # '3.0.0-alpha1' -__version__ = '3.0.2-alpha2' +__version__ = '3.0.2-alpha4' diff --git a/lib/vmwareconn.py b/lib/vmwareconn.py index 4352a99..1fd7d5c 100644 --- a/lib/vmwareconn.py +++ b/lib/vmwareconn.py @@ -1,7 +1,6 @@ import logging import ssl -from datetime import datetime, timedelta -from pyVmomi import vim, vmodl # type: ignore +from pyVmomi import vmodl # type: ignore from pyVim import connect from .asset_cache import AssetCache @@ -26,52 +25,6 @@ def get_data(ip4, username, password, obj_type, properties): return data -def get_perf(ip4, username, password, obj_type, metrics, interval): - conn = _get_conn(ip4, username, password) - content = conn.RetrieveContent() - content_time = conn.CurrentTime() - view_ref = content.viewManager.CreateContainerView( - container=content.rootFolder, type=[obj_type], recursive=True) - - perf_manager = content.perfManager - counters_lk = {c.key: c for c in perf_manager.perfCounter} - - results = {} - for child in view_ref.view: - available = perf_manager.QueryAvailablePerfMetric(entity=child) - - metric_id = [ - vim.PerformanceManager.MetricId(counterId=m.counterId, - instance=m.instance) - for m in available - if m.counterId in counters_lk and ( - counters_lk[m.counterId].groupInfo.key, - counters_lk[m.counterId].nameInfo.key) in metrics - ] - if len(metric_id) == 0: - # nothing to query - continue - - end_time = content_time - start_time = content_time - timedelta(seconds=interval + 1) - spec = vim.PerformanceManager.QuerySpec(intervalId=20, - entity=child, - metricId=metric_id, - startTime=start_time, - endTime=end_time) - results[child.config.instanceUuid] = result = {m: {} for m in metrics} - for stat in perf_manager.QueryStats(querySpec=[spec]): - for val in stat.value: - counter = counters_lk[val.id.counterId] - path = counter.groupInfo.key, counter.nameInfo.key - instance = val.id.instance - value = val.value - result[path][instance] = value - - view_ref.Destroy() - return results - - def drop_connnection(host): conn, _ = AssetCache.get_value((host, 'connection')) if conn: diff --git a/lib/vmwarequery.py b/lib/vmwarequery.py index c43a4fb..8458dbc 100644 --- a/lib/vmwarequery.py +++ b/lib/vmwarequery.py @@ -2,12 +2,11 @@ import logging from http.client import BadStatusLine from libprobe.asset import Asset -from libprobe.exceptions import CheckException, IgnoreCheckException, \ - IgnoreResultException +from libprobe.exceptions import CheckException from pyVmomi import vim # type: ignore -from typing import List, Tuple +from typing import List -from .vmwareconn import get_content, get_data, get_perf, drop_connnection +from .vmwareconn import get_content, get_data, drop_connnection DEFAULT_INTERVAL = 300 @@ -22,8 +21,8 @@ async def vmwarequery_content( username = asset_config.get('username') password = asset_config.get('password') if None in (username, password): - logging.error(f'missing credentails for {asset}') - raise IgnoreResultException + msg = 'missing credentails' + raise CheckException(msg) try: result = await asyncio.get_event_loop().run_in_executor( @@ -33,60 +32,14 @@ async def vmwarequery_content( username, password, ) + except CheckException: + raise except (vim.fault.InvalidLogin, vim.fault.NotAuthenticated): - logging.error(f'invalid credentails for {asset}') - raise IgnoreResultException - except vim.fault.HostConnectFault: - msg = 'Failed to connect.' - raise CheckException(msg) - except (IOError, - BadStatusLine, - ConnectionError) as e: - msg = str(e) or e.__class__.__name__ - drop_connnection(address) - raise CheckException(msg) - except Exception as e: - msg = str(e) or e.__class__.__name__ - logging.exception(msg) + msg = 'invalid login or not authenticated' raise CheckException(msg) - else: - return result - - -async def vmwarequery_perf( - asset: Asset, - asset_config: dict, - check_config: dict, - obj_type: vim.ManagedEntity, - metrics: List[Tuple[str, str]] = []) -> list: - address = check_config.get('address') - if not address: - address = asset.name - username = asset_config.get('username') - password = asset_config.get('password') - if None in (username, password): - logging.error(f'missing credentails for {asset}') - raise IgnoreResultException - interval = check_config.get('_interval', DEFAULT_INTERVAL) - - try: - result = await asyncio.get_event_loop().run_in_executor( - None, - get_perf, - address, - username, - password, - obj_type, - metrics, - interval, - ) - except (vim.fault.InvalidLogin, - vim.fault.NotAuthenticated): - logging.error(f'invalid credentails for {asset}') - raise IgnoreResultException except vim.fault.HostConnectFault: - msg = 'Failed to connect.' + msg = 'failed to connect.' raise CheckException(msg) except (IOError, BadStatusLine, @@ -114,8 +67,8 @@ async def vmwarequery( username = asset_config.get('username') password = asset_config.get('password') if None in (username, password): - logging.error(f'missing credentails for {asset}') - raise IgnoreResultException + msg = 'missing credentails' + raise CheckException(msg) try: result = await asyncio.get_event_loop().run_in_executor( @@ -127,16 +80,14 @@ async def vmwarequery( obj_type, properties, ) - except (CheckException, - IgnoreCheckException, - IgnoreResultException): + except CheckException: raise except (vim.fault.InvalidLogin, vim.fault.NotAuthenticated): - logging.error(f'invalid credentails for {asset}') - raise IgnoreResultException + msg = 'invalid login or not authenticated' + raise CheckException(msg) except vim.fault.HostConnectFault: - msg = 'Failed to connect.' + msg = 'failed to connect.' raise CheckException(msg) except (IOError, BadStatusLine, diff --git a/requirements.txt b/requirements.txt index 4df0c4a..0a15bdb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ libprobe==0.2.33 -pyvmomi==8.0.0.1 +pyvmomi==8.0.1.0.2