VMware Cloud Community
Capital20111014
Contributor
Contributor

How do I add a controller to a freshly made VM using the Web API?

Hi,

Tired of banging my head against this for 2 days, wonder if someone could point me on the right track. Smiley Wink

I have been using the ESX Web API for a week or so now, using .NET. (I'll save you my opinion on it as it's not good (the API, not .NET!)...Smiley Happy)

I Am able to List/Start/Stop VM's no problem.

I now wish to create a VM and add a RDM disk.

Create the VM: no problem.

Add the disk: BIIIG PROBLEMS

1) If I try and add the disk, I get "Device Requires A Controller"

2) If I try and add the controller, I get "Invalid Backing Information For Device"

I have found (on this forum) samples for doing this that ALWAYS assume the machine HAS a SCSI controller (it then loops through the hardware to find it and uses its ControllerKey).

My VM, being freshly made and having never had a disk assigned, does not have a SCSI controller. I have no way of finding the controller key,

So, here is my question:

After a successful call to CreateVM_Task, how do I add a RDM to the created VM, remembering that there is no SCSI controller on the VM at this point?

(I would be soooo pleased with a code example, in any language you choose!)

Thanks in advance for any assistance.

Reply
0 Kudos
2 Replies
mattboren
Expert
Expert

Greetings, @Capital-

I recently had a similar need, too.  Using PowerShell and PowerCLI, one can definitely accomplish this.

To add a SCSI controller to a given VM (here, a VM with no SCSI controllers), you can do some:

## get .Net View object of VM to which to add SCSI controller

$vmToAddStorageTo = Get-VM "myNewVM"
$viewVMToAddStorageTo = Get-View $vmToAddStorageTo
## if no controller on Bus 0 present, this should add one by the name "SCSI controller 0"
$vmcsConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
## array of Config Specs (could add multiple items at once, if desired)
$vmcsConfigSpec.DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec[] (1)
$vmcsConfigSpec.DeviceChange[0] = New-Object VMware.Vim.VirtualDeviceConfigSpec
$vmcsConfigSpec.DeviceChange[0].operation = "add"
## new LSI Logic controller
$vmcsConfigSpec.DeviceChange[0].device = New-Object VMware.Vim.VirtualLsiLogicController
## default value given by VMware, apparently (all SCSI controllers I inspected had this value)
$vmcsConfigSpec.DeviceChange[0].device.controllerKey = 100
## the SCSI Bus number
$vmcsConfigSpec.DeviceChange[0].device.busNumber = 0
$vmcsConfigSpec.DeviceChange[0].device.sharedBus = "noSharing"
## do the actual VM reconfig to add the SCSI controller; using "ReconfigVM_Task()" version as the next step needs for the task to be complete
$viewVMToAddStorageTo.ReconfigVM($vmcsConfigSpec)

This adds a LSI Logic controller as Bus number 0.  You could add other controller types on other bus numbers as desired.

Then, to add the RDM as a new disk on the VM, you can use the New-HardDisk cmdlet.  It just requires the RDM's DeviceName, which is of the form "/vmfs/devices/disks/naa.01234567890abcdef0123456789abcde" , try:

## the WWN of the RDM to use -- can be retrieved from vSphere via PowerShell, or via the vSphere Client $strRDMWWN = "01234567890abcdef0123456789abcde" ## the .Net View object for a VMHost that sees the given disk for the RDM $viewHostWithStorage = Get-View -ViewType HostSystem -Filter @{"name" = "myESXHostName"} ## get the DeviceName of the RDM; to be used in the New-HardDisk cmdlet $strRDMDiskDeviceName = ($viewHostWithStorage.Config.StorageDevice.ScsiLun | ?{$_.DeviceName -like "*${strRDMWWN}"}).DeviceName $sctlControllerToUse = $vmToAddStorageTo | Get-ScsiController | ?{$_.Name -eq $strSecondScsiControllerDefaultName} New-HardDisk -Controller $sctlControllerToUse -DiskType rawPhysical -DeviceName $strRDMDiskDeviceName -VM $vmToAddStorageTo

Substitute, of course, your device WWN and VMHost name.  And, if you already have the RDM's DeviceName, the step to get it can be skipped, and the DeviceName can be entered directly.

As you see, this utilizes some API calls, and some PowerCLI cmdlets.  A bit more research should reveal how to do this via strictly API calls.  And, if API-calls-only is the needed mechanism, using the Onyx tool (http://communities.vmware.com/community/vmtn/vsphere/automationtools/onyx) will help you immensely.

Enjoy.

message updated by mattboren:  corrected inconsistent/errant variable name, added comment to code

Capital20111014
Contributor
Contributor

Thank you for responding.

I was missing the Add operation type (assumed it would be default!) for the controller.

Your post was very helpful! Thanks.

HOW TO CREATE A VM AND ADD A RDM USING VB.NET

        ''' <summary>
        ''' Creates a Virtual Machine on the connected to ESX box
        ''' </summary>
        Public Function CreateVirtualMachine(ByVal VMName As String) As Boolean


            'Starts a Virtual Machine given the Name

            Try


                Dim Args() As String
                ReDim Args(5)

                'Set up the common argument array as ESX would like.
                Args(0) = "--url"
                Args(1) = MyURL
                Args(2) = "--username"
                Args(3) = MyUserName
                Args(4) = "--password"
                Args(5) = MyPassword

                'Create the option array for the Start VM command
                Dim UserOpts(7) As AppUtil.OptionSpec

                'Set up the Option Array
                UserOpts(0) = New AppUtil.OptionSpec("vmname", "String", 1, "Blah", VMName)
                UserOpts(1) = New AppUtil.OptionSpec("datacentername", "String", 1, "Blah", "")
                UserOpts(2) = New AppUtil.OptionSpec("hostname", "String", 0, "Blah", "")

                UserOpts(3) = New AppUtil.OptionSpec("guestosid", "String", 0, "Blah", "winXPProGuest")
                UserOpts(4) = New AppUtil.OptionSpec("cpucount", "String", 0, "Blah", "4")
                UserOpts(5) = New AppUtil.OptionSpec("disksize", "String", 0, "Blah", "524288")
                UserOpts(6) = New AppUtil.OptionSpec("memorysize", "String", 0, "Blah", "2048")
                UserOpts(7) = New AppUtil.OptionSpec("datastorename", "String", 0, "Blah", "")

                UtilityObject = AppUtil.ExtendedAppUtil.initialize("VMCreate", UserOpts, Args)
                UtilityObject.connect()

                'Create a VM Object
                Dim VMObject As Vim25Api.ManagedObjectReference

                'Create the Host Management object that we will use to start the VM
                Dim HostManageObject As Vim25Api.ManagedObjectReference


                ReDim Args(9)
                Args(0) = "--url"
                Args(1) = MyURL
                Args(2) = "--username"
                Args(3) = MyUserName
                Args(4) = "--password"
                Args(5) = MyPassword

                Args(6) = "--vmname"
                Args(7) = VMName
                Args(8) = "--datacentername"
                Args(9) = ""


                'Get the UtilityObject for accessing the API, telling it we wish to do a Power Operation
                UtilityObject = AppUtil.ExtendedAppUtil.initialize("VMCreate", UserOpts, Args)
                UtilityObject.connect()

                'Get the Host System from the API
                HostManageObject = UtilityObject.getServiceUtilV25().GetFirstDecendentMoRef(Nothing, "HostSystem")

                Dim DataCenterObject As Vim25Api.ManagedObjectReference = UtilityObject.getServiceUtilV25().GetFirstDecendentMoRef(Nothing, "Datacenter")
                Dim HostFolderObject As Vim25Api.ManagedObjectReference = UtilityObject.getServiceUtilV25().GetMoRefProp(DataCenterObject, "hostFolder")
                Dim CompResourceARR As ArrayList = UtilityObject.getServiceUtilV25().GetDecendentMoRefs(HostFolderObject, "ComputeResource")
                Dim VMUtil As New AppUtil.VMUtils(UtilityObject)
                Dim CompResources() As Object = DirectCast(CompResourceARR.ToArray(GetType(Object)), Object())

                Dim OurCompResource As Vim25Api.ManagedObjectReference

                If CompResources.Length = 1 Then
                    Dim ResArray As Array = CompResources(0)
                    OurCompResource = ResArray(0)
                End If

                Dim EnvBrowseObject As Vim25Api.ManagedObjectReference = UtilityObject.getServiceUtilV25().GetMoRefProp(OurCompResource, "environmentBrowser")

                Dim ESXConfigObject As Vim25Api.ConfigTarget = UtilityObject.getServiceConnectionV25.Service.QueryConfigTarget(EnvBrowseObject, HostManageObject)
                Dim SCSIDisk() As Vim25Api.VirtualMachineScsiDiskDeviceInfo = ESXConfigObject.scsiDisk

                Dim ResourcePoolObject As Vim25Api.ManagedObjectReference = UtilityObject.getServiceUtilV25().GetMoRefProp(OurCompResource, "resourcePool")
                Dim VMFolderObject As Vim25Api.ManagedObjectReference = UtilityObject.getServiceUtilV25().GetMoRefProp(DataCenterObject, "vmFolder")
                Dim VMConf As New Vim25Api.VirtualMachineConfigSpec

                VMConf.name = VMName
                VMConf.annotation = "Created for source server " + VMName

                VMConf.memoryMB = 4096
                VMConf.memoryMBSpecified = True

                VMConf.numCPUs = 4
                VMConf.numCPUsSpecified = True

                VMConf.files = New Vim25Api.VirtualMachineFileInfo
                VMConf.files.vmPathName = "[ccl-dev-001:storage1] " + VMName + "/" + VMName + ".vmx"

                VMConf.guestId = "winnetstandardguest"

                Dim DeviceConfigs(1) As Vim25Api.VirtualDeviceConfigSpec
                DeviceConfigs(0) = New Vim25Api.VirtualDeviceConfigSpec

                With DeviceConfigs(0)

                    Dim VC As New Vim25Api.VirtualLsiLogicController

                    VC.busNumber = 0
                    VC.key = 100
                    VC.sharedBus = Vim25Api.VirtualSCSISharing.noSharing

                    .operation = Vim25Api.VirtualDeviceConfigSpecOperation.add
                    .operationSpecified = True
                    .device = VC

                End With

                Dim TaskObject As Vim25Api.ManagedObjectReference = UtilityObject.getServiceConnectionV25.Service.CreateVM_Task(VMFolderObject, VMConf, ResourcePoolObject, HostManageObject)
                Dim Res As String = UtilityObject.getServiceUtilV25.WaitForTask(TaskObject)

                Dim NewVMObject As Vim25Api.ManagedObjectReference
                Dim NewVMConf As Vim25Api.VirtualMachineConfigInfo

                Try

                    'Get the VM from the Host
                    NewVMObject = UtilityObject.getServiceUtilV25().GetDecendentMoRef(Nothing, "VirtualMachine", VMName)
                    NewVMConf = UtilityObject.getServiceUtilV25().GetDynamicProperty(NewVMObject, "config")

                Catch ex As Exception

                    LastErr = ex.Message
                    LastRes = False
                    Exit Function

                End Try

                VMConf.deviceChange = DeviceConfigs

                Dim ReConfTask As Vim25Api.ManagedObjectReference = UtilityObject.getServiceConnectionV25.Service.ReconfigVM_Task(NewVMObject, VMConf)
                Dim ReconfRes As String = UtilityObject.getServiceUtilV25.WaitForTask(ReConfTask)


                Try

                    'Get the VM from the Host
                    NewVMObject = UtilityObject.getServiceUtilV25().GetDecendentMoRef(Nothing, "VirtualMachine", VMName)
                    NewVMConf = UtilityObject.getServiceUtilV25().GetDynamicProperty(NewVMObject, "config")

                Catch ex As Exception

                    LastErr = ex.Message
                    LastRes = False
                    Exit Function

                End Try

                Dim NewCtrlKey As Integer

                For Each X As Vim25Api.VirtualDevice In NewVMConf.hardware.device
                    If X.deviceInfo.label.Contains("SCSI") Then
                        NewCtrlKey = X.key
                        Exit For
                    End If
                Next

                DeviceConfigs(0) = New Vim25Api.VirtualDeviceConfigSpec
                With DeviceConfigs(0)

                    Dim VD As New Vim25Api.VirtualDisk
                    Dim VDBacking As New Vim25Api.VirtualDiskRawDiskMappingVer1BackingInfo

                    VDBacking.deviceName = SCSIDisk(0).disk.canonicalName
                    VDBacking.compatibilityMode = "physicalMode"
                    VDBacking.uuid = SCSIDisk(0).disk.uuid
                    VDBacking.fileName = "[ccl-dev-001:storage1] " + VMName + "/" + VMName + ".vmdk"

                    VD.controllerKey = NewCtrlKey
                    VD.controllerKeySpecified = True

                    VD.capacityInKB = 1024
                    VD.key = -101

                    VD.unitNumber = 0
                    VD.unitNumberSpecified = True

                    VD.backing = VDBacking
                    .device = VD

                    .operation = Vim25Api.VirtualDeviceConfigSpecOperation.add
                    .operationSpecified = True

                    .fileOperation = Vim25Api.VirtualDeviceConfigSpecFileOperation.create
                    .fileOperationSpecified = True

                End With

                VMConf.deviceChange = DeviceConfigs

                Dim ReConfTask2 As Vim25Api.ManagedObjectReference = UtilityObject.getServiceConnectionV25.Service.ReconfigVM_Task(NewVMObject, VMConf)
                Dim ReconfRes2 As String = UtilityObject.getServiceUtilV25.WaitForTask(ReConfTask)

                UtilityObject.disConnect()

                'We worked OK so reset our error variables
                LastRes = True
                LastErr = ""

            Catch ex As Exception

                'We failed due to a runtime error, reset our error variables
                LastErr = ex.Message
                LastRes = False

            End Try


        End Function

Reply
0 Kudos