├── .gitignore ├── LICENSE ├── README.md ├── py-vminfo-web.py ├── py-vminfo.py ├── python-vmstats-web2.png ├── viconfig.template ├── vm-win-stats-py3.png └── vminfo-launch.html /.gitignore: -------------------------------------------------------------------------------- 1 | viconfig.py 2 | python-vmstats-web2.png 3 | vm-win-stats-py3.png 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 lgeeklee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-vmstats 2 | ============== 3 | 4 | Python script using pyVmomi to get VM statistics 5 | 6 | Command line version: 7 | 8 | Example output 9 | 10 | The script requires the following parameters: 11 | 12 | -s HOST, --host HOST : Remote host to connect to. 13 | 14 | -u USER, --user USER : User name to use when connecting to host. 15 | 16 | -p PASSWORD, --password PASSWORD : Password to use when connecting to host. 17 | 18 | -m VM, --vm VM : Virtual Machine to report. 19 | 20 | -i INT, --interval INT : Interval to average the vSphere stats over in minutes 21 | 22 | -c, --cert_check_skip : Skip ssl certificate check 23 | 24 | The -p/--password is now optional and if not provided on the command line will prompt instead. 25 | 26 | 27 | The web version: 28 | 29 | Example output 30 | 31 | 32 | -------------------------------------------------------------------------------- /py-vminfo-web.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | 5 | """ 6 | Python program that generates various statistics for one or more virtual machines 7 | 8 | A list of virtual machines can be provided as a comma separated list. 9 | """ 10 | 11 | from __future__ import print_function 12 | from pyVim.connect import SmartConnect, Disconnect 13 | from pyVmomi import vmodl, vim 14 | from datetime import timedelta, datetime 15 | 16 | import atexit 17 | import getpass 18 | import cgi 19 | import viconfig 20 | import ssl 21 | 22 | form = cgi.FieldStorage() 23 | print("Content-Type: text/html;charset=utf-8\n\n") 24 | 25 | 26 | def BuildQuery(content, vchtime, counterId, instance, vm, interval): 27 | perfManager = content.perfManager 28 | metricId = vim.PerformanceManager.MetricId(counterId=counterId, instance=instance) 29 | startTime = vchtime - timedelta(minutes=(interval + 1)) 30 | endTime = vchtime - timedelta(minutes=1) 31 | query = vim.PerformanceManager.QuerySpec(intervalId=20, entity=vm, metricId=[metricId], startTime=startTime, 32 | endTime=endTime) 33 | perfResults = perfManager.QueryPerf(querySpec=[query]) 34 | if perfResults: 35 | return perfResults 36 | else: 37 | print('ERROR: Performance results empty. TIP: Check time drift on source and vCenter server') 38 | print('Troubleshooting info:') 39 | print('vCenter/host date and time: {}'.format(vchtime)) 40 | print('Start perf counter time : {}'.format(startTime)) 41 | print('End perf counter time : {}'.format(endTime)) 42 | print(query) 43 | exit() 44 | 45 | 46 | def html_table(vm_property, vm_value): 47 | print('') 48 | print('' + vm_property + '') 49 | print('' + str(vm_value) + '') 50 | print('') 51 | 52 | 53 | 54 | def PrintVmInfo(vm, content, vchtime, interval, perf_dict): 55 | statInt = interval * 3 # There are 3 20s samples in each minute 56 | summary = vm.summary 57 | disk_list = [] 58 | network_list = [] 59 | 60 | # Convert limit and reservation values from -1 to None 61 | if vm.resourceConfig.cpuAllocation.limit == -1: 62 | vmcpulimit = "None" 63 | else: 64 | vmcpulimit = "{} Mhz".format(vm.resourceConfig.cpuAllocation.limit) 65 | if vm.resourceConfig.memoryAllocation.limit == -1: 66 | vmmemlimit = "None" 67 | else: 68 | vmmemlimit = "{} MB".format(vm.resourceConfig.memoryAllocation.limit) 69 | 70 | if vm.resourceConfig.cpuAllocation.reservation == 0: 71 | vmcpures = "None" 72 | else: 73 | vmcpures = "{} Mhz".format(vm.resourceConfig.cpuAllocation.reservation) 74 | if vm.resourceConfig.memoryAllocation.reservation == 0: 75 | vmmemres = "None" 76 | else: 77 | vmmemres = "{} MB".format(vm.resourceConfig.memoryAllocation.reservation) 78 | 79 | vm_hardware = vm.config.hardware 80 | for each_vm_hardware in vm_hardware.device: 81 | if (each_vm_hardware.key >= 2000) and (each_vm_hardware.key < 3000): 82 | disk_list.append('{} | {:.1f}GB | Thin: {} | {}'.format(each_vm_hardware.deviceInfo.label, 83 | each_vm_hardware.capacityInKB/1024/1024, 84 | each_vm_hardware.backing.thinProvisioned, 85 | each_vm_hardware.backing.fileName)) 86 | elif (each_vm_hardware.key >= 4000) and (each_vm_hardware.key < 5000): 87 | network_list.append('{} | {} | {}'.format(each_vm_hardware.deviceInfo.label, 88 | each_vm_hardware.deviceInfo.summary, 89 | each_vm_hardware.macAddress)) 90 | 91 | disk_output = '
'.join(disk_list) 92 | network_output = '
'.join(network_list) 93 | 94 | #CPU Ready Average 95 | statCpuReady = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'cpu.ready.summation')), "", vm, interval) 96 | cpuReady = (float(sum(statCpuReady[0].value[0].value)) / statInt) 97 | #CPU Usage Average % - NOTE: values are type LONG so needs divided by 100 for percentage 98 | statCpuUsage = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'cpu.usage.average')), "", vm, interval) 99 | cpuUsage = ((float(sum(statCpuUsage[0].value[0].value)) / statInt) / 100) 100 | #Memory Active Average MB 101 | statMemoryActive = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.active.average')), "", vm, interval) 102 | memoryActive = (float(sum(statMemoryActive[0].value[0].value) / 1024) / statInt) 103 | #Memory Shared 104 | statMemoryShared = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.shared.average')), "", vm, interval) 105 | memoryShared = (float(sum(statMemoryShared[0].value[0].value) / 1024) / statInt) 106 | #Memory Balloon 107 | statMemoryBalloon = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.vmmemctl.average')), "", vm, interval) 108 | memoryBalloon = (float(sum(statMemoryBalloon[0].value[0].value) / 1024) / statInt) 109 | #Memory Swapped 110 | statMemorySwapped = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.swapped.average')), "", vm, interval) 111 | memorySwapped = (float(sum(statMemorySwapped[0].value[0].value) / 1024) / statInt) 112 | #Datastore Average IO 113 | statDatastoreIoRead = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.numberReadAveraged.average')), 114 | "*", vm, interval) 115 | DatastoreIoRead = (float(sum(statDatastoreIoRead[0].value[0].value)) / statInt) 116 | statDatastoreIoWrite = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.numberWriteAveraged.average')), 117 | "*", vm, interval) 118 | DatastoreIoWrite = (float(sum(statDatastoreIoWrite[0].value[0].value)) / statInt) 119 | #Datastore Average Latency 120 | statDatastoreLatRead = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.totalReadLatency.average')), 121 | "*", vm, interval) 122 | DatastoreLatRead = (float(sum(statDatastoreLatRead[0].value[0].value)) / statInt) 123 | statDatastoreLatWrite = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.totalWriteLatency.average')), 124 | "*", vm, interval) 125 | DatastoreLatWrite = (float(sum(statDatastoreLatWrite[0].value[0].value)) / statInt) 126 | 127 | #Network usage (Tx/Rx) 128 | statNetworkTx = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'net.transmitted.average')), "", vm, interval) 129 | networkTx = (float(sum(statNetworkTx[0].value[0].value) * 8 / 1024) / statInt) 130 | statNetworkRx = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'net.received.average')), "", vm, interval) 131 | networkRx = (float(sum(statNetworkRx[0].value[0].value) * 8 / 1024) / statInt) 132 | 133 | 134 | print('''\ 135 | 162 | ''') 163 | 164 | print('') 165 | 166 | print('

NOTE: Any VM statistics are averages of the last {} minutes

'.format(statInt / 3)) 167 | print('

Core Information

') 168 | html_table('Virtual Machine Name', ' {} '.format(summary.config.name)) 169 | html_table('Description', summary.config.annotation) 170 | html_table('Guest', summary.config.guestFullName) 171 | if vm.rootSnapshot: 172 | html_table('Snapshot Status', 'Snapshot(s) found') 173 | else: 174 | html_table('Snapshot Status', 'No Snapshots') 175 | html_table('VM .vmx Path', summary.config.vmPathName) 176 | html_table('Virtual Disks', disk_output) 177 | html_table('Virtual NIC(s)', network_output) 178 | print('
') 179 | print('

vCPU and Memory Information

') 180 | print('') 181 | html_table('[VM] Limits', 'CPU: {}, Memory: {}'.format(vmcpulimit, vmmemlimit)) 182 | html_table('[VM] Reservations', 'CPU: {}, Memory: {}'.format(vmcpures, vmmemres)) 183 | html_table('[VM] Number of vCPUs', summary.config.numCpu) 184 | html_table('[VM] CPU Ready', 'Average {:.1f} %, Maximum {:.1f} %'.format((cpuReady / 20000 * 100), 185 | ((float(max(statCpuReady[0].value[0] 186 | .value)) / 20000 * 100)))) 187 | html_table('[VM] CPU (%)', '{:.0f} %'.format(cpuUsage)) 188 | html_table('[VM] Memory', '{} MB ({:.1f} GB)'.format(summary.config.memorySizeMB, 189 | (float(summary.config.memorySizeMB) / 1024))) 190 | html_table('[VM] Memory Shared', '{:.0f} %, {:.0f} MB'.format(((memoryShared / summary.config.memorySizeMB) * 100), 191 | memoryShared)) 192 | html_table('[VM] Memory Balloon', '{:.0f} %, {:.0f} MB'.format(((memoryBalloon / summary.config.memorySizeMB) 193 | * 100), memoryBalloon)) 194 | html_table('[VM] Memory Swapped', '{:.0f} %, {:.0f} MB'.format(((memorySwapped / summary.config.memorySizeMB) 195 | * 100), memorySwapped)) 196 | html_table('[VM] Memory Active', '{:.0f} %, {:.0f} MB'.format(((memoryActive / summary.config.memorySizeMB) * 100), 197 | memoryActive)) 198 | print('
') 199 | print('

Datastore and Network Information

') 200 | print('') 201 | html_table('[VM] Datastore Average IO', 'Read: {:.0f} IOPS, Write: {:.0f} IOPS'.format(DatastoreIoRead, 202 | DatastoreIoWrite)) 203 | html_table('[VM] Datastore Average Latency', 'Read: {:.0f} ms, Write: {:.0f} ms'.format(DatastoreLatRead, 204 | DatastoreLatWrite)) 205 | html_table('[VM] Overall Network Usage', 'Transmitted {:.3f} Mbps, Received {:.3f} Mbps'.format(networkTx, networkRx)) 206 | print('
') 207 | print('

Parent Host Information

') 208 | print('') 209 | html_table('[Host] Name', summary.runtime.host.name) 210 | html_table('[Host] CPU Detail', 'Processor Sockets: {}, Cores per Socket {}'.format( 211 | summary.runtime.host.summary.hardware.numCpuPkgs, 212 | (summary.runtime.host.summary.hardware.numCpuCores / summary.runtime.host.summary.hardware.numCpuPkgs))) 213 | html_table('[Host] CPU Type', summary.runtime.host.summary.hardware.cpuModel) 214 | html_table('[Host] CPU Usage', 'Used: {} Mhz, Total: {} Mhz'.format( 215 | summary.runtime.host.summary.quickStats.overallCpuUsage, 216 | (summary.runtime.host.summary.hardware.cpuMhz * summary.runtime.host.summary.hardware.numCpuCores))) 217 | html_table('[Host] Memory Usage ', 'Used: {:.0f} GB, Total: {:.0f} GB\n'.format( 218 | (float(summary.runtime.host.summary.quickStats.overallMemoryUsage) / 1024), 219 | (float(summary.runtime.host.summary.hardware.memorySize) / 1024 / 1024 / 1024))) 220 | 221 | print('
') 222 | 223 | 224 | def StatCheck(perf_dict, counter_name): 225 | counter_key = perf_dict[counter_name] 226 | return counter_key 227 | 228 | 229 | def GetProperties(content, viewType, props, specType): 230 | # Build a view and get basic properties for all Virtual Machines 231 | objView = content.viewManager.CreateContainerView(content.rootFolder, viewType, True) 232 | tSpec = vim.PropertyCollector.TraversalSpec(name='tSpecName', path='view', skip=False, type=vim.view.ContainerView) 233 | pSpec = vim.PropertyCollector.PropertySpec(all=False, pathSet=props, type=specType) 234 | oSpec = vim.PropertyCollector.ObjectSpec(obj=objView, selectSet=[tSpec], skip=False) 235 | pfSpec = vim.PropertyCollector.FilterSpec(objectSet=[oSpec], propSet=[pSpec], reportMissingObjectsInResults=False) 236 | retOptions = vim.PropertyCollector.RetrieveOptions() 237 | totalProps = [] 238 | retProps = content.propertyCollector.RetrievePropertiesEx(specSet=[pfSpec], options=retOptions) 239 | totalProps += retProps.objects 240 | while retProps.token: 241 | retProps = content.propertyCollector.ContinueRetrievePropertiesEx(token=retProps.token) 242 | totalProps += retProps.objects 243 | objView.Destroy() 244 | # Turn the output in retProps into a usable dictionary of values 245 | gpOutput = [] 246 | for eachProp in totalProps: 247 | propDic = {} 248 | for prop in eachProp.propSet: 249 | propDic[prop.name] = prop.val 250 | propDic['moref'] = eachProp.obj 251 | gpOutput.append(propDic) 252 | return gpOutput 253 | 254 | 255 | def main(): 256 | args = viconfig.GetArgs() 257 | try: 258 | vmnames = form['vmname'].value 259 | si = None 260 | if args['password']: 261 | password = args['password'] 262 | else: 263 | password = getpass.getpass(prompt="Enter password for host {} and user {}: ".format(args['host'], args['user'])) 264 | try: 265 | context = ssl._create_unverified_context() 266 | si = SmartConnect(host=args['host'], 267 | user=args['user'], 268 | pwd=password, 269 | port=int(args['port']), 270 | sslContext=context) 271 | except IOError as e: 272 | pass 273 | if not si: 274 | print('Could not connect to the specified host using specified username and password') 275 | return -1 276 | 277 | atexit.register(Disconnect, si) 278 | content = si.RetrieveContent() 279 | # Get vCenter date and time for use as baseline when querying for counters 280 | vchtime = si.CurrentTime() 281 | 282 | # Get all the performance counters 283 | perf_dict = {} 284 | perfList = content.perfManager.perfCounter 285 | for counter in perfList: 286 | counter_full = "{}.{}.{}".format(counter.groupInfo.key, counter.nameInfo.key, counter.rollupType) 287 | perf_dict[counter_full] = counter.key 288 | 289 | retProps = GetProperties(content, [vim.VirtualMachine], ['name', 'runtime.powerState'], vim.VirtualMachine) 290 | 291 | #Find VM supplied as arg and use Managed Object Reference (moref) for the PrintVmInfo 292 | for vm in retProps: 293 | if (vm['name'] in vmnames) and (vm['runtime.powerState'] == "poweredOn"): 294 | PrintVmInfo(vm['moref'], content, vchtime, int(form['vminterval'].value), perf_dict) 295 | elif vm['name'] in vmnames: 296 | print('ERROR: Problem connecting to Virtual Machine. {} is likely powered off or suspended'.format(vm['name'])) 297 | 298 | except vmodl.MethodFault as e: 299 | print('Caught vmodl fault : ' + e.msg) 300 | return -1 301 | except Exception as e: 302 | print('Caught exception : ' + str(e)) 303 | return -1 304 | 305 | return 0 306 | 307 | # Start program 308 | if __name__ == "__main__": 309 | main() 310 | -------------------------------------------------------------------------------- /py-vminfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Python program that generates various statistics for one or more virtual machines 5 | 6 | A list of virtual machines can be provided as a comma separated list. 7 | """ 8 | 9 | from __future__ import print_function 10 | from pyVim.connect import SmartConnect, Disconnect 11 | from pyVmomi import vmodl, vim 12 | from datetime import timedelta, datetime 13 | 14 | import argparse 15 | import atexit 16 | import getpass 17 | 18 | import ssl 19 | 20 | def GetArgs(): 21 | """ 22 | Supports the command-line arguments listed below. 23 | """ 24 | parser = argparse.ArgumentParser(description='Process args for retrieving all the Virtual Machines') 25 | parser.add_argument('-s', '--host', required=True, action='store', help='Remote host to connect to') 26 | parser.add_argument('-o', '--port', type=int, default=443, action='store', help='Port to connect on') 27 | parser.add_argument('-u', '--user', required=True, action='store', help='User name to use when connecting to host') 28 | parser.add_argument('-p', '--password', required=False, action='store', 29 | help='Password to use when connecting to host') 30 | parser.add_argument('-m', '--vm', required=True, action='store', help='On eor more Virtual Machines to report on') 31 | parser.add_argument('-c', '--cert_check_skip', required=False, action='store_true', help='skip ssl certificate check') 32 | parser.add_argument('-i', '--interval', type=int, default=15, action='store', 33 | help='Interval to average the vSphere stats over') 34 | args = parser.parse_args() 35 | return args 36 | 37 | 38 | def BuildQuery(content, vchtime, counterId, instance, vm, interval): 39 | perfManager = content.perfManager 40 | metricId = vim.PerformanceManager.MetricId(counterId=counterId, instance=instance) 41 | startTime = vchtime - timedelta(minutes=(interval + 1)) 42 | endTime = vchtime - timedelta(minutes=1) 43 | query = vim.PerformanceManager.QuerySpec(intervalId=20, entity=vm, metricId=[metricId], startTime=startTime, 44 | endTime=endTime) 45 | perfResults = perfManager.QueryPerf(querySpec=[query]) 46 | if perfResults: 47 | return perfResults 48 | else: 49 | print('ERROR: Performance results empty. TIP: Check time drift on source and vCenter server') 50 | print('Troubleshooting info:') 51 | print('vCenter/host date and time: {}'.format(vchtime)) 52 | print('Start perf counter time : {}'.format(startTime)) 53 | print('End perf counter time : {}'.format(endTime)) 54 | print(query) 55 | exit() 56 | 57 | 58 | 59 | def PrintVmInfo(vm, content, vchtime, interval, perf_dict, ): 60 | statInt = interval * 3 # There are 3 20s samples in each minute 61 | summary = vm.summary 62 | disk_list = [] 63 | network_list = [] 64 | 65 | # Convert limit and reservation values from -1 to None 66 | if vm.resourceConfig.cpuAllocation.limit == -1: 67 | vmcpulimit = "None" 68 | else: 69 | vmcpulimit = "{} Mhz".format(vm.resourceConfig.cpuAllocation.limit) 70 | if vm.resourceConfig.memoryAllocation.limit == -1: 71 | vmmemlimit = "None" 72 | else: 73 | vmmemlimit = "{} MB".format(vm.resourceConfig.memoryAllocation.limit) 74 | 75 | if vm.resourceConfig.cpuAllocation.reservation == 0: 76 | vmcpures = "None" 77 | else: 78 | vmcpures = "{} Mhz".format(vm.resourceConfig.cpuAllocation.reservation) 79 | if vm.resourceConfig.memoryAllocation.reservation == 0: 80 | vmmemres = "None" 81 | else: 82 | vmmemres = "{} MB".format(vm.resourceConfig.memoryAllocation.reservation) 83 | 84 | vm_hardware = vm.config.hardware 85 | for each_vm_hardware in vm_hardware.device: 86 | if (each_vm_hardware.key >= 2000) and (each_vm_hardware.key < 3000): 87 | disk_list.append('{} | {:.1f}GB | Thin: {} | {}'.format(each_vm_hardware.deviceInfo.label, 88 | each_vm_hardware.capacityInKB/1024/1024, 89 | each_vm_hardware.backing.thinProvisioned, 90 | each_vm_hardware.backing.fileName)) 91 | elif (each_vm_hardware.key >= 4000) and (each_vm_hardware.key < 5000): 92 | network_list.append('{} | {} | {}'.format(each_vm_hardware.deviceInfo.label, 93 | each_vm_hardware.deviceInfo.summary, 94 | each_vm_hardware.macAddress)) 95 | 96 | #CPU Ready Average 97 | statCpuReady = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'cpu.ready.summation')), "", vm, interval) 98 | cpuReady = (float(sum(statCpuReady[0].value[0].value)) / statInt) 99 | #CPU Usage Average % - NOTE: values are type LONG so needs divided by 100 for percentage 100 | statCpuUsage = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'cpu.usage.average')), "", vm, interval) 101 | cpuUsage = ((float(sum(statCpuUsage[0].value[0].value)) / statInt) / 100) 102 | #Memory Active Average MB 103 | statMemoryActive = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.active.average')), "", vm, interval) 104 | memoryActive = (float(sum(statMemoryActive[0].value[0].value) / 1024) / statInt) 105 | #Memory Shared 106 | statMemoryShared = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.shared.average')), "", vm, interval) 107 | memoryShared = (float(sum(statMemoryShared[0].value[0].value) / 1024) / statInt) 108 | #Memory Balloon 109 | statMemoryBalloon = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.vmmemctl.average')), "", vm, interval) 110 | memoryBalloon = (float(sum(statMemoryBalloon[0].value[0].value) / 1024) / statInt) 111 | #Memory Swapped 112 | statMemorySwapped = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'mem.swapped.average')), "", vm, interval) 113 | memorySwapped = (float(sum(statMemorySwapped[0].value[0].value) / 1024) / statInt) 114 | #Datastore Average IO 115 | statDatastoreIoRead = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.numberReadAveraged.average')), 116 | "*", vm, interval) 117 | DatastoreIoRead = (float(sum(statDatastoreIoRead[0].value[0].value)) / statInt) 118 | statDatastoreIoWrite = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.numberWriteAveraged.average')), 119 | "*", vm, interval) 120 | DatastoreIoWrite = (float(sum(statDatastoreIoWrite[0].value[0].value)) / statInt) 121 | #Datastore Average Latency 122 | statDatastoreLatRead = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.totalReadLatency.average')), 123 | "*", vm, interval) 124 | DatastoreLatRead = (float(sum(statDatastoreLatRead[0].value[0].value)) / statInt) 125 | statDatastoreLatWrite = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'datastore.totalWriteLatency.average')), 126 | "*", vm, interval) 127 | DatastoreLatWrite = (float(sum(statDatastoreLatWrite[0].value[0].value)) / statInt) 128 | 129 | #Network usage (Tx/Rx) 130 | statNetworkTx = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'net.transmitted.average')), "", vm, interval) 131 | networkTx = (float(sum(statNetworkTx[0].value[0].value) * 8 / 1024) / statInt) 132 | statNetworkRx = BuildQuery(content, vchtime, (StatCheck(perf_dict, 'net.received.average')), "", vm, interval) 133 | networkRx = (float(sum(statNetworkRx[0].value[0].value) * 8 / 1024) / statInt) 134 | 135 | print('\nNOTE: Any VM statistics are averages of the last {} minutes\n'.format(statInt / 3)) 136 | print('Server Name :', summary.config.name) 137 | print('Description :', summary.config.annotation) 138 | print('Guest :', summary.config.guestFullName) 139 | if vm.rootSnapshot: 140 | print('Snapshot Status : Snapshots present') 141 | else: 142 | print('Snapshot Status : No Snapshots') 143 | print('VM .vmx Path :', summary.config.vmPathName) 144 | try: 145 | print('Virtual Disks :', disk_list[0]) 146 | if len(disk_list) > 1: 147 | disk_list.pop(0) 148 | for each_disk in disk_list: 149 | print(' ', each_disk) 150 | except IndexError: 151 | pass 152 | print('Virtual NIC(s) :', network_list[0]) 153 | if len(network_list) > 1: 154 | network_list.pop(0) 155 | for each_vnic in network_list: 156 | print(' ', each_vnic) 157 | print('[VM] Limits : CPU: {}, Memory: {}'.format(vmcpulimit, vmmemlimit)) 158 | print('[VM] Reservations : CPU: {}, Memory: {}'.format(vmcpures, vmmemres)) 159 | print('[VM] Number of vCPUs :', summary.config.numCpu) 160 | print('[VM] CPU Ready : Average {:.1f} %, Maximum {:.1f} %'.format((cpuReady / 20000 * 100), 161 | ((float(max( 162 | statCpuReady[0].value[ 163 | 0].value)) / 20000 * 100)))) 164 | print('[VM] CPU (%) : {:.0f} %'.format(cpuUsage)) 165 | print('[VM] Memory : {} MB ({:.1f} GB)'.format(summary.config.memorySizeMB, (float(summary.config.memorySizeMB) / 1024))) 166 | print('[VM] Memory Shared : {:.0f} %, {:.0f} MB'.format( 167 | ((memoryShared / summary.config.memorySizeMB) * 100), memoryShared)) 168 | print('[VM] Memory Balloon : {:.0f} %, {:.0f} MB'.format( 169 | ((memoryBalloon / summary.config.memorySizeMB) * 100), memoryBalloon)) 170 | print('[VM] Memory Swapped : {:.0f} %, {:.0f} MB'.format( 171 | ((memorySwapped / summary.config.memorySizeMB) * 100), memorySwapped)) 172 | print('[VM] Memory Active : {:.0f} %, {:.0f} MB'.format( 173 | ((memoryActive / summary.config.memorySizeMB) * 100), memoryActive)) 174 | print('[VM] Datastore Average IO : Read: {:.0f} IOPS, Write: {:.0f} IOPS'.format(DatastoreIoRead, 175 | DatastoreIoWrite)) 176 | print('[VM] Datastore Average Latency : Read: {:.0f} ms, Write: {:.0f} ms'.format(DatastoreLatRead, 177 | DatastoreLatWrite)) 178 | print('[VM] Overall Network Usage : Transmitted {:.3f} Mbps, Received {:.3f} Mbps'.format(networkTx, networkRx)) 179 | print('[Host] Name : {}'.format(summary.runtime.host.name)) 180 | print('[Host] CPU Detail : Processor Sockets: {}, Cores per Socket {}'.format( 181 | summary.runtime.host.summary.hardware.numCpuPkgs, 182 | (summary.runtime.host.summary.hardware.numCpuCores / summary.runtime.host.summary.hardware.numCpuPkgs))) 183 | print('[Host] CPU Type : {}'.format(summary.runtime.host.summary.hardware.cpuModel)) 184 | print('[Host] CPU Usage : Used: {} Mhz, Total: {} Mhz'.format( 185 | summary.runtime.host.summary.quickStats.overallCpuUsage, 186 | (summary.runtime.host.summary.hardware.cpuMhz * summary.runtime.host.summary.hardware.numCpuCores))) 187 | print('[Host] Memory Usage : Used: {:.0f} GB, Total: {:.0f} GB\n'.format( 188 | (float(summary.runtime.host.summary.quickStats.overallMemoryUsage) / 1024), 189 | (float(summary.runtime.host.summary.hardware.memorySize) / 1024 / 1024 / 1024))) 190 | 191 | 192 | def StatCheck(perf_dict, counter_name): 193 | counter_key = perf_dict[counter_name] 194 | return counter_key 195 | 196 | 197 | def GetProperties(content, viewType, props, specType): 198 | # Build a view and get basic properties for all Virtual Machines 199 | objView = content.viewManager.CreateContainerView(content.rootFolder, viewType, True) 200 | tSpec = vim.PropertyCollector.TraversalSpec(name='tSpecName', path='view', skip=False, type=vim.view.ContainerView) 201 | pSpec = vim.PropertyCollector.PropertySpec(all=False, pathSet=props, type=specType) 202 | oSpec = vim.PropertyCollector.ObjectSpec(obj=objView, selectSet=[tSpec], skip=False) 203 | pfSpec = vim.PropertyCollector.FilterSpec(objectSet=[oSpec], propSet=[pSpec], reportMissingObjectsInResults=False) 204 | retOptions = vim.PropertyCollector.RetrieveOptions() 205 | totalProps = [] 206 | retProps = content.propertyCollector.RetrievePropertiesEx(specSet=[pfSpec], options=retOptions) 207 | totalProps += retProps.objects 208 | while retProps.token: 209 | retProps = content.propertyCollector.ContinueRetrievePropertiesEx(token=retProps.token) 210 | totalProps += retProps.objects 211 | objView.Destroy() 212 | # Turn the output in retProps into a usable dictionary of values 213 | gpOutput = [] 214 | for eachProp in totalProps: 215 | propDic = {} 216 | for prop in eachProp.propSet: 217 | propDic[prop.name] = prop.val 218 | propDic['moref'] = eachProp.obj 219 | gpOutput.append(propDic) 220 | return gpOutput 221 | 222 | 223 | def main(): 224 | args = GetArgs() 225 | try: 226 | vmnames = args.vm 227 | si = None 228 | if args.password: 229 | password = args.password 230 | else: 231 | password = getpass.getpass(prompt="Enter password for host {} and user {}: ".format(args.host, args.user)) 232 | try: 233 | if args.cert_check_skip: 234 | context = ssl._create_unverified_context() 235 | si = SmartConnect(host=args.host, 236 | user=args.user, 237 | pwd=password, 238 | port=int(args.port), 239 | sslContext=context) 240 | else: 241 | si = SmartConnect(host=args.host, 242 | user=args.user, 243 | pwd=password, 244 | port=int(args.port)) 245 | except IOError as e: 246 | pass 247 | if not si: 248 | print('Could not connect to the specified host using specified username and password') 249 | return -1 250 | 251 | atexit.register(Disconnect, si) 252 | content = si.RetrieveContent() 253 | # Get vCenter date and time for use as baseline when querying for counters 254 | vchtime = si.CurrentTime() 255 | 256 | # Get all the performance counters 257 | perf_dict = {} 258 | perfList = content.perfManager.perfCounter 259 | for counter in perfList: 260 | counter_full = "{}.{}.{}".format(counter.groupInfo.key, counter.nameInfo.key, counter.rollupType) 261 | perf_dict[counter_full] = counter.key 262 | 263 | retProps = GetProperties(content, [vim.VirtualMachine], ['name', 'runtime.powerState'], vim.VirtualMachine) 264 | 265 | #Find VM supplied as arg and use Managed Object Reference (moref) for the PrintVmInfo 266 | for vm in retProps: 267 | if (vm['name'] in vmnames) and (vm['runtime.powerState'] == "poweredOn"): 268 | PrintVmInfo(vm['moref'], content, vchtime, args.interval, perf_dict) 269 | elif vm['name'] in vmnames: 270 | print('ERROR: Problem connecting to Virtual Machine. {} is likely powered off or suspended'.format(vm['name'])) 271 | 272 | except vmodl.MethodFault as e: 273 | print('Caught vmodl fault : ' + e.msg) 274 | return -1 275 | except Exception as e: 276 | print('Caught exception : ' + str(e)) 277 | return -1 278 | 279 | return 0 280 | 281 | # Start program 282 | if __name__ == "__main__": 283 | main() 284 | -------------------------------------------------------------------------------- /python-vmstats-web2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgeeklee/python-vmstats/fc5ab24fe0585bede1febfaf4c94d750078a5763/python-vmstats-web2.png -------------------------------------------------------------------------------- /viconfig.template: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | 5 | def GetArgs(): 6 | """ 7 | Supports the command-line arguments listed below. 8 | """ 9 | 10 | args = dict(host='', 11 | port=443, 12 | user='', 13 | password='',) 14 | 15 | return args 16 | 17 | 18 | def main(): 19 | pass 20 | 21 | 22 | if __name__ == "__main__": 23 | main() -------------------------------------------------------------------------------- /vm-win-stats-py3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgeeklee/python-vmstats/fc5ab24fe0585bede1febfaf4c94d750078a5763/vm-win-stats-py3.png -------------------------------------------------------------------------------- /vminfo-launch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | py-vminfo 9 | 10 | 11 | 12 |
13 |

Virtual Machine Name:
14 |

Statistics Interval (minutes):
15 |
16 | 17 |

18 | 19 | 20 | --------------------------------------------------------------------------------