#!/usr/bin/env python3 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. from pyVim.connect import SmartConnect, SmartConnectNoSSL, Disconnect from pyVmomi import vim import atexit import sys import argparse import json isDebugLogs = False hostClusterNameDict = {} pgHostNameDict = {} networksDict = {} def log_message(msg): if isDebugLogs == True: print(msg) def get_clusters(content, cluster=None): if cluster is not None: log_message("Getting clusters (name=" + cluster + ") ...") else: log_message("Getting clusters ...") cluster_view = content.viewManager.CreateContainerView(content.rootFolder, [vim.ClusterComputeResource], True) clusters = [] if cluster is not None: for c in cluster_view.view: if c.name == cluster: clusters.append(c) hosts = c.host for host in hosts: hostClusterNameDict[host.name] = c.name break else: for c in cluster_view.view: clusters.append(c) hosts = c.host for host in hosts: hostClusterNameDict[host.name] = c.name cluster_view.Destroy() log_message('\t{} cluster(s) found'.format(len(clusters))) for c in clusters: log_message('\t' + c.name) return clusters def get_vm_hosts(clusters): log_message("Getting ESX hosts ...") hosts = [] for cluster in clusters: hosts.extend(cluster.host) log_message('\t{} host(s) found'.format(len(hosts))) for host in hosts: log_message('\t' + host.name) return hosts def get_vms(content): log_message("Getting VMs ...") vm_view = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True) obj = [vm for vm in vm_view.view] vm_view.Destroy() return obj def get_hosts_port_groups(hosts): log_message("Collecting portGroups on hosts. This may take a while ...") hostPgDict = {} for host in hosts: pgs = host.config.network.portgroup hostPgDict[host] = pgs for pg in pgs: pgHostNameDict[pg.spec.name] = host.name log_message("\tHost {} done.".format(host.name)) log_message("\tPortgroup collection complete.") return hostPgDict def get_vm_info(vm, hostPgDict): vmPowerState = vm.runtime.powerState log_message('\tVM: ' + vm.name + '(' + vmPowerState + ')') get_vm_nics(vm, hostPgDict) def get_vm_nics(vm, hostPgDict): try: for dev in vm.config.hardware.device: if isinstance(dev, vim.vm.device.VirtualEthernetCard): dev_backing = dev.backing portGroup = None vlanId = None isolatedPvlan = None isolatedPvlanType = None vSwitch = None if hasattr(dev_backing, 'port'): portGroupKey = dev.backing.port.portgroupKey dvsUuid = dev.backing.port.switchUuid try: dvs = content.dvSwitchManager.QueryDvsByUuid(dvsUuid) except: log_message('\tError: Unable retrieve details for distributed vSwitch ' + dvsUuid) portGroup = '' vlanId = '' vSwitch = '' else: pgObj = dvs.LookupDvPortGroup(portGroupKey) portGroup = pgObj.config.name try: if isinstance(pgObj.config.defaultPortConfig.vlan, vim.dvs.VmwareDistributedVirtualSwitch.PvlanSpec): for pvlanConfig in dvs.config.pvlanConfig: if pvlanConfig.secondaryVlanId == pgObj.config.defaultPortConfig.vlan.pvlanId: vlanId = str(pvlanConfig.primaryVlanId) isolatedPvlanType = pvlanConfig.pvlanType isolatedPvlan = str(pgObj.config.defaultPortConfig.vlan.pvlanId) break else: vlanId = str(pgObj.config.defaultPortConfig.vlan.vlanId) except AttributeError: log_message('\tError: Unable retrieve details for portgroup ' + portGroup) vlanId = '' vSwitch = str(dvs.name) else: portGroup = dev.backing.network.name vmHost = vm.runtime.host # global variable hostPgDict stores portGroups per host pgs = hostPgDict[vmHost] for p in pgs: if portGroup in p.key: vlanId = str(p.spec.vlanId) vSwitch = str(p.spec.vswitchName) if portGroup is None: portGroup = '' if vlanId is None: vlanId = '' vmHostName = None vmClusterName = None try: vmHostName = vm.runtime.host.name except AttributeError: vmHostName = '' try: vmClusterName = vm.runtime.host.parent.name except AttributeError: vmClusterName = '' add_network(portGroup, vlanId, isolatedPvlanType, isolatedPvlan, vSwitch, vm.name, dev.deviceInfo.label, dev.macAddress, vmClusterName, vmHostName) log_message('\t\t' + dev.deviceInfo.label + '->' + dev.macAddress + ' @ ' + vSwitch + '->' + portGroup + ' (VLAN ' + vlanId + ')') except AttributeError: log_message('\tError: Unable retrieve details for ' + vm.name) def add_network(portGroup, vlanId, isolatedPvlanType, isolatedPvlan, vSwitch, vmName, vmDeviceLabel, vmMacAddress, vmClusterName, vmHostName): key = vSwitch + '->' + portGroup + ' (VLAN ' + vlanId + ')' device = {"label": vmDeviceLabel, "macaddress": vmMacAddress} vm = {"name":vmName, "device": device} if key in networksDict: network = networksDict[key] network["virtualmachines"].append(vm) networksDict[key] = network else: vms = [vm] try: host = pgHostNameDict[portGroup] except KeyError: host = vmHostName try: cluster = hostClusterNameDict[host] except KeyError: cluster = vmClusterName network = {"portgroup": portGroup, "cluster": cluster, "host": host, "switch": vSwitch, "virtualmachines": vms} if vlanId != '': network["vlanid"] = vlanId if isolatedPvlan is not None: network["isolatedpvlan"] = isolatedPvlan if isolatedPvlanType is not None: network["isolatedpvlantype"] = isolatedPvlanType networksDict[key] = network def get_args(): parser = argparse.ArgumentParser( description='Arguments for talking to vCenter') parser.add_argument('-s', '--host', required=True, action='store', help='vSpehre service to connect to') parser.add_argument('-o', '--port', type=int, default=443, action='store', help='Port to connect on') parser.add_argument('-u', '--user', required=True, action='store', help='User name to use') parser.add_argument('-p', '--password', required=False, action='store', help='Password to use') parser.add_argument('-c', '--cluster', required=False, action='store', help='Cluster for which discover networks') parser.add_argument('-S', '--disable_ssl_verification', required=False, action='store_true', help='Disable ssl host certificate verification') parser.add_argument('-d', '--debug', required=False, action='store_true', help='Debug log messages') args = parser.parse_args() return args def main(): global content, isDebugLogs, hostClusterNameDict, pgHostNameDict, networksDict args = get_args() if args.password: password = args.password else: password = getpass.getpass(prompt='Enter password for host %s and ' 'user %s: ' % (args.host, args.user)) if args.debug: isDebugLogs = True if args.disable_ssl_verification: serviceInstance = SmartConnectNoSSL(host=args.host, user=args.user, pwd=password, port=int(args.port)) else: serviceInstance = SmartConnect(host=args.host, user=args.user, pwd=password, port=int(args.port)) atexit.register(Disconnect, serviceInstance) content = serviceInstance.RetrieveContent() if args.cluster: clusters = get_clusters(content, args.cluster) else: clusters = get_clusters(content) hosts = [] if len(clusters) > 0: hosts = get_vm_hosts(clusters) if len(hosts) > 0: hostPgDict = get_hosts_port_groups(hosts) vms = get_vms(content) log_message('\t{} VM(s) found'.format(len(vms))) for vm in vms: get_vm_info(vm, hostPgDict) networks = list(networksDict.values()) response = {"count": len(networks), "networks": networks} print(json.dumps(response, indent=2, sort_keys=True)) # Main section if __name__ == "__main__": sys.exit(main())