VMware vSphere

 View Only
  • 1.  Getting all USB devices connected to VCenter

    Posted Jan 10, 2019 04:37 PM

    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.



  • 2.  RE: Getting all USB devices connected to VCenter

    Posted Nov 01, 2020 04:31 PM

    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 ?



  • 3.  RE: Getting all USB devices connected to VCenter

    Posted Nov 02, 2020 07:35 AM

    Hi,

    what is your problem? Are all your ESXi in the same datacenter and cluster?



  • 4.  RE: Getting all USB devices connected to VCenter

    Posted Nov 02, 2020 08:17 AM

    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.



  • 5.  RE: Getting all USB devices connected to VCenter

    Posted Nov 02, 2020 09:06 AM

    Ok. I try to explain the different parts of the script. Hopefully this will help you.

    Here you connect to your VCenter/vSphere Server.

    1. service_instance = connect.SmartConnect(host=<Ip of your vSphere server>, 
    2.                                             user=<userName>, 
    3.                                             pwd=<password>, 
    4.                                             port=<your port>) 
    5.  
    6.      
    7.     si = service_instance.content.searchIndex 
    8.     atexit.register(connect.Disconnect, si) 
    9.  
    10.     # get your datacenter 
    11.      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.

    1.     dc = None 
    2.     for datacenter in content.rootFolder.childEntity: 
    3.         if datacenter.name != <name of datacenter>: 
    4.             continue 
    5.         dc = datacenter 
    6.         break 
    7. # just some error checking         
    8.     if dc is None
    9.         print("did not find the datacenter"
    10.         return 0 
    11.     

    Here i get all available ESXi in the datacenter. The CreateContainerView function does everything in this case.

    1.     viewType = [vim.HostSystem]  # object types to look for 
    2.     recursive = True  # whether we should look into it recursively    
    3.     containerView = content.viewManager.CreateContainerView(dc, viewType, recursive) 
    4.     children = containerView.view 
    5.     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.

    1.     # get the compute resource and vm list 
    2.     container = content.viewManager.CreateContainerView(dc, [vim.ComputeResource], True
    3.     containerVM = content.viewManager.CreateContainerView(dc, [vim.VirtualMachine], True
    4.      
    5.     vmList = [] 
    6.     for vm in containerVM.view: 
    7.         vmList.append(vm) 
    8.     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.

    1.    # get my current agent/VM on which i am running 
    2.     IP = socket.gethostbyname(socket.gethostname()) 
    3.     myself = None 
    4.     myself = si.FindByIp(None, IP, True
    5.     hosts = {} 
    6.     devices = [] 
    7.     keysAlreadyConnected = {} 
    8.      
    9.     print("--------------------"
    10.     print("searching for all USB devices available in the datacenter"
    11.     print("--------------------"

    This is for writing a log file. You can replace all f.write() with print() calls to get the ouput directly

    1.     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.

    1.         for child in children: 
    2.             if len(child.vm) < 1
    3.                 print("no vms, so no key checking for {0}".format(child.name)) 
    4.                 continue 
    5.             if <specific esxi name> not in child.name: 
    6.                 continue 
    7.             f.write("host: {0}".format(child.name)) 
    8.             hosts[child.name] = child 
    9.              

    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.

    1.             # get a compute resource to get the environmentBrowser to be able to run QueryConfigTarget 
    2.             obj = None 
    3.             for c in container.view: 
    4.                 if c.name == child.parent.name: 
    5.                     obj = c 
    6.                     break 
    7.             if obj is None
    8.                 print("no compute resource found"
    9.                 continue  
    10.             envBrowser = obj.environmentBrowser 
    11.             if envBrowser is None
    12.                 print("no env browser"
    13.                 return 0 
    14.             result = envBrowser.QueryConfigTarget(child) 
    15.             # end of workaround 
    16.              
    17.   

    The result of QueryConfigTarget() is the complete configuration of the ESXi. I only take the list of USB device and check them.          

    1.             if result is not None
    2.                 f.write("no usb devices: {0}\n".format(len(result.usb))) 
    3.                 for usbDevice in result.usb: 
    4. # just some log output
    5.                     deviceName = usbDevice.description 
    6.                     f.write("\n\n" + usbDevice.description + "\n"
    7.                     f.write(usbDevice.physicalPath+ "\n"
    8.                     f.write(usbDevice.name+ "\n"
    9.                     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.

    1.                     if <my specific device name> in deviceName: 
    2.                          # this only works for VMs that are turned on 
    3.                         if usbDevice.summary is not None
    4.                             connectedVM = usbDevice.summary 
    5.                             if connectedVM is not None
    6.                                 f.write("connected to {0}\n".format(connectedVM.config.name)) 
    7.                                 vmName = connectedVM.config.name 
    8.                                 # this is only for debugging and is for using only specific agents/VMs 
    9.                                 if <specific VM name>  in vmName: 
    10.                                     devices.append(usbDevice.physicalPath) 
    11.                                     print("will check {0} from vm {1}".format(deviceName, vmName)) 
    12.                         else
    13.                             devices.append(usbDevice.physicalPath) 
    14.                             print(usbDevice.summary) 
    15.                             print("will check {0} that is not connected".format(deviceName)) 

    Here I create a map of USB devices that are connected to other VMs

    1.           # check vms to look for connected usb devices on powered off VMs 
    2.              for vm1 in vmList: 
    3.                  if vm1.name == myself.name: 
    4.                      continue 
    5.                  for device in vm1.config.hardware.device: 
    6.                      if not isinstance(device, vim.vm.device.VirtualUSB): 
    7.                     continue 
    8.                 if device.backing.deviceName in devices: 
    9.                     keysAlreadyConnected[device.backing.deviceName] = vm1 
    10.                  
    11.             else
    12.                 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



  • 6.  RE: Getting all USB devices connected to VCenter

    Posted Nov 13, 2020 05:32 PM

    That worked for me

    Thank You for helping and for the detailed explanation.