mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
* refactor: remove trailing whitespace from Python files * Add the GitHub Super-Linter Add Python flake8 linting for W291 trailing whitespace * Add licenses
288 lines
11 KiB
Python
Executable File
288 lines
11 KiB
Python
Executable File
#!/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())
|