Hello,
currently I have a problem, which involves vSphere/VCenter. I have a VCenter with many USB devices connected to different ESXi and I have to connect an USB device to a specific VM on demand. Now I need to know the USB path to these devices. The one that looks like this
deviceName = "path:1/0/3/3/2/5/1 version:2"
I can connect to the ESXi and get this path for device that are already connected to VMs with this command. The second command gives me the ID of the VM, which replaces XXXX in the first command
vim-cmd vmsvc/device.getdevices XXXX | grep path
vim-cmd vmsvc/getallvms
Now the problem is that I do not get the paths for USB devices, that are not connected to a VM. I know that I can connect a device with the device ID, that i get with lsusb, but the problem here is that all my device have the same VendorID and ProductID, because there are the same product and from the same vendor. So the vSphere Client can differentiate between all USB devices and knows which are connected to which ESXi etc. But I did not find any way to get this information via vSphere Web API or some ESXi vim-cmd.
Are there any ways to get this information?
I am using vSphere 5.5
Kind regards
Update:
So i found a solution for this
I used python for this and you need these two libs for it pyVim and pyVmomi. These are used to communicate with the vSphere Server and everything
Here is the code
from pyVim import connect
from pyVmomi import vmodl
from pyVmomi import vim
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)
# get your datacenter
content = service_instance.RetrieveContent()
dc = None
for datacenter in content.rootFolder.childEntity:
if datacenter.name != <name of datacenter>:
continue
dc = datacenter
break
if dc is None:
print("did not find the datacenter")
return 0
viewType = [vim.HostSystem] # object types to look for
recursive = True # whether we should look into it recursively
containerView = content.viewManager.CreateContainerView(dc, viewType, recursive)
children = containerView.view
containerView.Destroy()
# get the compute resource and vm list
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()
# get my current agent/VM on which i am running
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("--------------------")
with open("log", 'w') as f:
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
# get a compute resource to get the environmentBrowser to be able to run QueryConfigTarget
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)
# end of workaround
if result is not None:
f.write("no usb devices: {0}\n".format(len(result.usb)))
for usbDevice in result.usb:
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")
if <my specific device name> in deviceName:
# this only works for VMs that are turned on
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
# this is only for debugging and is for using only specific agents/VMs
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))
# check vms to look for connected usb devices on powered off 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))
This works for me. I think these Python libs are also available for Java and this solution should work there too.
The indention is probably messed up but I hope this will help everyone with the same problem.
Here is the code to add or remove a usb device
def addOrRemoveAllDevicesFromCurrentAgent(vm, deviceList, remove = False):
vm_spec = vim.vm.ConfigSpec()
usb_changes = []
for device in deviceList:
usb_spec = vim.vm.device.VirtualDeviceSpec()
if remove:
usb_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove
else:
usb_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
usb_spec.device = device
usb_changes.append(usb_spec)
vm_spec.deviceChange = usb_changes
e = vm.ReconfigVM_Task(spec=vm_spec)
x = 0
while not e.info.completeTime and x < 30:
time.sleep(1)
x = x + 1
This should be understandable and is pretty straight forward.
Hi, @testing321
I have the same case, i have 3 ESXI hosts and per 1 VM on them. Also i have USB devices connected to every ESXI host and i need to passthrough them to every VM.
I tried to run your script but it didn’t work me. Since I’m new with python i can’t understand what problem it have.
Could you help me with this ?
Hi,
what is your problem? Are all your ESXi in the same datacenter and cluster?
Hi, testing321
Yes, all of my ESXI hosts located in the same datacenter and cluster.
Every host have connected USB devices which should be passed through to VM. Also every ESXI host have only 1 VM per host.
My problem that i don’t know how i can do this. I tried to run you script, but it didn’t work. As I’m new with python it’s hard for me to understand where problem in script is.
Ok. I try to explain the different parts of the script. Hopefully this will help you.
Here you connect to your VCenter/vSphere Server.
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.
Here i get all available ESXi in the datacenter. The CreateContainerView function does everything in this case.
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.
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.
This is for writing a log file. You can replace all f.write() with print() calls to get the ouput directly
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.
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.
The result of QueryConfigTarget() is the complete configuration of the ESXi. I only take the list of USB device and check them.
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.
Here I create a map of USB devices that are connected to other VMs
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
That worked for me
Thank You for helping and for the detailed explanation.