The Hyper-V API - Disks
In an earlier post in this series (several posts ago now) I showed how the Msvm_virtualSystemManagementService WMI object can be used to configure resources in Hyper-V, and I started with the easy step of setting memory and CPUs which exist on a freshly created virtual machine. What about Network Cards, SCSI controllers, Disk drives and so on ? The 4 steps are the same as I outlined before i.e.
- Get a ResourceAllocationSettingData (RASD) object
- Modify one or more of its properties.
- Covert it to XML formatted Text,
- Pass the XMl as one of an array of arguments to one of the Methods of the Msvm_virtualSystemManagementService.
Where before I was using the ModifyVirtualSystemResources method, new resources need the AddVirtualSystemResources. Where memory and CPU had special ResourceAllocationSettingData (RASD) objects these ones are more generic and I have a "Get-vmRASD" function. This takes type and subtype arguments, here's a quick summary of them
Resource Type | Resource Sub Type |
6 | Microsoft Synthetic SCSI Controller |
10 | Microsoft Emulated Ethernet Port |
10 | Microsoft Synthetic Ethernet Port |
16 | Microsoft Synthetic DVD Drive |
21 | Microsoft Virtual CD/DVD Disk |
21 | Microsoft Virtual Floppy Disk |
21 | Microsoft Virtual Hard Disk |
22 | Microsoft Synthetic Disk Drive |
So lets see how we use them in practice. As before I get an object for theVirtual System management service
$VSMgtSvc=Get-WmiObject -NameSpace "root\virtualization" -Class "MsVM_virtualSystemManagementService"
If I want to create a SCSI controller, the 4 steps above translate to:
$SCSIRASD=Get-VMRASD -ResType 6 -ResSubType 'Microsoft Synthetic SCSI Controller'
$SCSIRASD.elementName="VMBus SCSI Controller"
$arguments = @($VM.__Path, @( $SCSIRASD.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
$VSMgtSvc.PSbase.InvokeMethod("AddVirtualSystemResources", $arguments)
If I want to create a disk drive I change the type of Resource Allocation settings data, I either use
$diskRASD=Get-VMRASD -ResType 22 -ResSubType 'Microsoft Synthetic Disk Drive'
if I want a hard drive or if I want a DVD drive it's:
$diskRASD=Get-VMRASD 16 'Microsoft Synthetic DVD Drive'
The remaining 3 steps are the same, this time I have to set two properties: a parent - the disk controller, and the address on the controller (passed as a parameter named $LUN)
$diskRASD.parent=(Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE).__Path
$diskRASD.address=$Lun
$arguments = @($VM.__Path, @( $diskRASD.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
$VSMgtSvc.PSbase.InvokeMethod("AddVirtualSystemResources", $arguments)
Then to mount a disk into the drive it's either
$diskRASD=Get-VMRASD -resType 21 -resSubType 'Microsoft Virtual CD/DVD Disk' -server $vm.__Server
if I want a Hard disk or if I want a DVD disk it's
$diskRASD=Get-VMRASD -resType 21 -resSubType 'Microsoft Virtual Hard Disk' -server $vm.__Server
And then as before the disk needs a parent - the drive it is mounted in, which makes more sense for a DVD than a Hard disk; and it needs a connection to the Virtual hard disk, ISO or Physical disk on the host and then we continue as before
$diskRASD.parent=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE) -Lun $lun ).__Path }
$diskRASD.Connection=$VHDPath
$arguments = @($VM.__Path, @( $diskRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
$VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)
OK: you may have noticed that I'm calling functions Get-VMRASD , Get-VMDiskController and Get-VMDrive
Get-VMDiskController returns the RASD object(s) for either SCSI or IDE disk controller(s) or for both. The only thing which is a bit awkward here is that SCSI controllers don't have an obvious ID, we just get a single RASD object if there is one controller or an array if there is more than one, so I get the one I want with | Select first | select last. I showed before how I can set-up filters to take input from the pipe, and the real version of this code does exactly that ... why will become clear in just a moment
Filter Get-VMDiskController
{Param ($VM , $ControllerID, [Switch]$SCSI, [Switch]$IDE )
if ($scsi) { $controllers=Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData
Where instanceId Like 'Microsoft:$($vm.name)%'
and resourceSubtype = 'Microsoft Synthetic SCSI Controller' " `
-NameSpace "root\virtualization"
if ($controllerID -eq $null) {$controllers}
else {$controllers | select -first ($controllerID + 1) | select -last 1} }
if ($IDE) { Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData
Where instanceId Like 'Microsoft:$($vm.name)%\\$ControllerID%'
and resourceSubtype = 'Microsoft Emulated IDE Controller' " -NameSpace "root\virtualization"}
}
Then I wrote Get-VM Drive which takes a controller as a parameter (and again I can pipe one or controller(s) in to that - though I've omitted the code out for the sake of space. )
Filter Get-VMDrive
{Param ($Controller, $LUN )
$CtrlPath=$Controller.__Path.replace("\","\\")
Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData
Where PARENT='$ctrlPath' and Address Like '$Lun%' " -NameSpace "root\virtualization" }
}
And finally I can pass the drive to Get-Disk, which looks like this
Filter Get-VMDisk
{Param ($Drive)
$DrivePath=$Drive.__Path.replace("\","\\")
Get-WmiObject -computerName $drive.__server -Query "Select * From MsVM_ResourceAllocationSettingData
Where PARENT='$DrivePath' " -NameSpace "root\virtualization"
}
So I can find the VHDs mounted used by one or more VMs with
Choose-VM -multiple | GetVmDiskController -IDE -SCSI | GetVMDrive | get-VMDisk
Or I can get more detailed information Like This
Function Get-VMDiskList
{Param ($vm)
foreach ($v in $vm) {
foreach ($dc in (get-vmdiskcontroller -vm $v -ide -scsi)) {
foreach ($drive in (get-vmdrive -controller $dc)) {
get-vmdisk -drive $drive | select-object -property `
@{name="VMName"; expression={$v.elementName}},
@{name="VMGUID"; expression={$v.Name}},
@{name="ControllerName"; expression={$dc.elementName}},
@{name="ControllerInstanceID"; expression={$dc.InstanceId}},
@{name="ControllerID"; expression={$dc.instanceID.split("\")[-1]}},
@{name="DriveName"; expression={$drive.caption}} ,
@{name="DriveInstanceID"; expression={$drive.instanceID}},
@{name="DriveLUN"; expression={$drive.address}},
@{name="DiskPath"; expression={$_.Connection}},
@{name="DiskName"; expression={$_.ElementName}},
@{name="DiskInstanceID"; expression={$_.InstanceID}} }}}
}
The Get-RASD code isn't nice. It's two WMI queries to get the name of the an object we need to create
Function Get-VMRASD
{Param ($ResType, $ResSubType)
$allocCapsPath= ((Get-WmiObject -NameSpace "root\virtualization" -Query "Select * From MsVM_AllocationCapabilities
Where ResourceType = $ResType AND ResourceSubType = '$ResSubType'").__Path).replace('\','\\')
New-Object System.Management.Managementobject((Get-WmiObject -ComputerName $server -NameSpace "root\virtualization"
-Query "Select * From MsVM_SettingsDefineCapabilities Where valuerange=0 and Groupcomponent = '$AllocCapsPath'").partcomponent)
}
Whilst it may not be nice, knowing the Values to pass it from the table above, the properties to set, and how to pass it into AddVirtualSystemResources allows you to create what ever disk related bits you need. In a future post I'll move on to looking at how you similar things with NICs. Then I'll have one more on creating VMs and setting the motherboard options and then I'll be posting the whole code for download - I've got some internal Microsoft people trying it out at the moment.
Technorati Tags: Microsoft,Windows Server 2008,Hyper-V,Virtualization,PowerShell,WMI
[Update the Original post had numerous proof reading errors - I hope there aren't any left]
Comments
- Anonymous
January 01, 2003
If you've read my post on adding disks to a Virtual machine , the techniques here should already feel