Hi,
Tired of banging my head against this for 2 days, wonder if someone could point me on the right track.
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!)...)
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.
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
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