Ok. I try to explain the different parts of the script. Hopefully this will help you.
Here you connect to your VCenter/vSphere Server.
- service_instance = connect.SmartConnect(host=<Ip of your vSphere server>,
- user=<userName>,
- pwd=<password>,
- port=<your port>)
-
-
- si = service_instance.content.searchIndex
- atexit.register(connect.Disconnect, si)
-
-
- content = service_instance.RetrieveContent()
Now you try to get the right datacenter. If you have multiple you iterate through all and look for the right one via the name. It seems you only have 1 and you can just pick the first item in the list.
- dc = None
- for datacenter in content.rootFolder.childEntity:
- if datacenter.name != <name of datacenter>:
- continue
- dc = datacenter
- break
- # just some error checking
- if dc is None:
- print("did not find the datacenter")
- return 0
-
Here i get all available ESXi in the datacenter. The CreateContainerView function does everything in this case.
- viewType = [vim.HostSystem]
- recursive = True
- containerView = content.viewManager.CreateContainerView(dc, viewType, recursive)
- children = containerView.view
- containerView.Destroy()
Now I get all VMs available in the datacenter and create a list of them. Again this works with the CreateContainerView function. I destroy the container I do not need anymore after i used them.
-
- container = content.viewManager.CreateContainerView(dc, [vim.ComputeResource], True)
- containerVM = content.viewManager.CreateContainerView(dc, [vim.VirtualMachine], True)
-
- vmList = []
- for vm in containerVM.view:
- vmList.append(vm)
- containerVM.Destroy()
If you running the script on a VM which is running on VCenter, this is getting the VM object of this agent. Myself will be the VM the script is running on. This only works if you use it on a VM that runs in the VCenter/Datacenter you are checking your USB devices.
-
- IP = socket.gethostbyname(socket.gethostname())
- myself = None
- myself = si.FindByIp(None, IP, True)
- hosts = {}
- devices = []
- keysAlreadyConnected = {}
-
- print("--------------------")
- print("searching for all USB devices available in the datacenter")
- print("--------------------")
This is for writing a log file. You can replace all f.write() with print() calls to get the ouput directly
- with open("log", 'w') as f:
Now iterate through all Hosts/ESXi. I start with some checks if the ESXi has VMs and if it is a specific ESXi I am looking for. Then I create a list of ESXi/Host objects.
- for child in children:
- if len(child.vm) < 1:
- print("no vms, so no key checking for {0}".format(child.name))
- continue
- if <specific esxi name> not in child.name:
- continue
- f.write("host: {0}".format(child.name))
- hosts[child.name] = child
-
Here I gete the environmentBrowser through a ComputeResource object. The parent of the ESXi object has to be the ComputeResource. I am not sure why it was that way. I just found this code online and it works for me. I think if you want to know more you have to check the vSphere API documentation.The environmentBrowser is used for the QueryConfigTarget() function.
-
- obj = None
- for c in container.view:
- if c.name == child.parent.name:
- obj = c
- break
- if obj is None:
- print("no compute resource found")
- continue
- envBrowser = obj.environmentBrowser
- if envBrowser is None:
- print("no env browser")
- return 0
- result = envBrowser.QueryConfigTarget(child)
-
-
-
The result of QueryConfigTarget() is the complete configuration of the ESXi. I only take the list of USB device and check them.
- if result is not None:
- f.write("no usb devices: {0}\n".format(len(result.usb)))
- for usbDevice in result.usb:
- # just some log output
- deviceName = usbDevice.description
- f.write("\n\n" + usbDevice.description + "\n")
- f.write(usbDevice.physicalPath+ "\n")
- f.write(usbDevice.name+ "\n")
- f.write(str(usbDevice.configurationTag)+ "\n")
Here I start checking the USB devices. I check for a specific name of a device and then check if it is connected to a VM. If the VM is running that the USB device it connected to, the USB device knows the VM - see usbDevice.summary -. If the VM is turned off the USB device does not know it. I create a list of USB devices.
- if <my specific device name> in deviceName:
-
- if usbDevice.summary is not None:
- connectedVM = usbDevice.summary
- if connectedVM is not None:
- f.write("connected to {0}\n".format(connectedVM.config.name))
- vmName = connectedVM.config.name
-
- if <specific VM name> in vmName:
- devices.append(usbDevice.physicalPath)
- print("will check {0} from vm {1}".format(deviceName, vmName))
- else:
- devices.append(usbDevice.physicalPath)
- print(usbDevice.summary)
- print("will check {0} that is not connected".format(deviceName))
Here I create a map of USB devices that are connected to other VMs
-
- for vm1 in vmList:
- if vm1.name == myself.name:
- continue
- for device in vm1.config.hardware.device:
- if not isinstance(device, vim.vm.device.VirtualUSB):
- continue
- if device.backing.deviceName in devices:
- keysAlreadyConnected[device.backing.deviceName] = vm1
-
- else:
- print ("did not find {0}".format(uuid))
At the end of this you should have a list and a map of your USB devices. Some parts are specific in my case because we have multiple Cluster with multiple ESXi. This is a limitation too. The ESXi must be in the same cluster. I know there is a setting to allow sharing of devices over multiple clusters, but I do not know if the script works with that.
Here is some code to add or remove a USB device to a VM. Before that you always have to login to the VCenter/vSphere server as seen at the top.
After logging in you can find the VM you want via the name like this:
# dc is the Datacenter object
clusterList = content.viewManager.CreateContainerView(
dc, [vim.ComputeResource], True)
agent = None
# find the VM
# there is no function to find it by the name provided by the API
for cluster in clusterList.view:
clusterListVM = content.viewManager.CreateContainerView(
cluster, [vim.VirtualMachine], True)
vmList = clusterListVM.view
clusterListVM.Destroy()
# finding the VM via the name
for vm in vmList:
if agentName == vm.name:
agent = vm
break
if agent is not None:
break
if agent is not None:
print("adding the key")
addOrRemoveKey(agent, keyPath)
print("added {0} to agent {1}".format(keyPath, agent.name))
----------------------------------------------------------------------------------------------------------
Here is the code for adding or removing a device:
def addOrRemoveKey(vm, device, remove=False):
"""
add or remove a single key to the specified agent
"""
usb_changes = []
vm_spec = vim.vm.ConfigSpec()
usb_spec = vim.vm.device.VirtualDeviceSpec()
# to not use keys that are blacklisted
if not checkKeyBlackListState(device, vm.summary.runtime.host.name):
print("avoid blacklisted key")
return False
if remove:
usb_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove
else:
usb_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
# check if the device is provided as a VirtualUSB object or as a string
# the string would be the usb path from the vSphere server. It looks like this:
# path: 1/2/3/4... verison: 2
if str == type(device):
usb_spec.device = vim.vm.device.VirtualUSB()
usb_spec.device.backing = vim.vm.device.VirtualUSB.USBBackingInfo()
usb_spec.device.backing.deviceName = device
else:
usb_spec.device = device
usb_changes.append(usb_spec)
# here the device gets attached or removed
vm_spec.deviceChange = usb_changes
e = vm.ReconfigVM_Task(spec=vm_spec)
# wait until the task is done
while e.info.state == vim.TaskInfo.State.running:
time.sleep(1)
# some error "handling"
if e.info.state == vim.TaskInfo.State.error:
msg = "Error adding {0} to {1}"
if remove == True:
msg = "Error removing {0} from {1}"
if str == type(device):
print(msg.format(device, vm.name))
else:
print(msg.format(device.backing.deviceName, vm.name))
print("--------------------------")
print("VCenter error message")
print(e.info.error)
print("--------------------------")
return False
return True