Skip to content

Commit

Permalink
Merge pull request #11 from iainbrighton/dev
Browse files Browse the repository at this point in the history
Breaking change: VM Generation and VHD/VHDX files. Fixes #4
  • Loading branch information
KarolKaczmarek committed Jul 1, 2015
2 parents 020882e + 96b678b commit 36e0ec1
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 37 deletions.
26 changes: 14 additions & 12 deletions DSCResources/MSFT_xVMHyperV/MSFT_xVMHyperV.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function Get-TargetResource
SwitchName = $vmObj.NetworkAdapters[0].SwitchName
State = $vmobj.State
Path = $vmobj.Path
Generation = if($vmobj.Generation -eq 1){"Vhd"}else{"Vhdx"}
Generation = $vmobj.Generation
StartupMemory = $vmobj.MemoryStartup
MinimumMemory = $vmobj.MemoryMinimum
MaximumMemory = $vmobj.MemoryMaximum
Expand Down Expand Up @@ -76,9 +76,9 @@ function Set-TargetResource
# Folder where the VM data will be stored
[String]$Path,

# Associated Virtual disk format - Vhd or Vhdx
[ValidateSet("Vhd","Vhdx")]
[String]$Generation = "Vhd",
# Virtual machine generation
[ValidateRange(1,2)]
[UInt32]$Generation = 1,

# Startup RAM for the VM
[ValidateRange(32MB,17342MB)]
Expand Down Expand Up @@ -222,11 +222,11 @@ function Set-TargetResource
$parameters = @{}
$parameters["Name"] = $Name
$parameters["VHDPath"] = $VhdPath
$parameters["Generation"] = $Generation

# Optional parameters
if($SwitchName){$parameters["SwitchName"]=$SwitchName}
if($Path){$parameters["Path"]=$Path}
if($Generation){$parameters["Generation"]=if($Generation -eq "Vhd"){1}else{2}}
$defaultStartupMemory = 512MB
if($StartupMemory){$parameters["MemoryStartupBytes"]=$StartupMemory}
elseif($MinimumMemory -and $defaultStartupMemory -lt $MinimumMemory){$parameters["MemoryStartupBytes"]=$MinimumMemory}
Expand Down Expand Up @@ -296,9 +296,9 @@ function Test-TargetResource
# Folder where the VM data will be stored
[String]$Path,

# Associated Virtual disk format - Vhd or Vhdx
[ValidateSet("Vhd","Vhdx")]
[String]$Generation = "Vhd",
# Virtual machine generation
[ValidateRange(1,2)]
[UInt32]$Generation = 1,

# Startup RAM for the VM
[ValidateRange(32MB,17342MB)]
Expand Down Expand Up @@ -369,12 +369,14 @@ function Test-TargetResource
Throw "StartupMemory($StartupMemory) should not be greater than MaximumMemory($MaximumMemory)"
}

# Check if the generation matches the VhdPath extenstion
if($Generation -and ($VhdPath.Split('.')[-1] -ne $Generation))
<# VM Generation has no direct relation to the virtual hard disk format and cannot be changed
after the virtual machine has been created. Generation 2 VMs do not support .VHD files. #>
if(($Generation -eq 2) -and ($VhdPath.Split('.')[-1] -eq 'vhd'))
{
Throw "Generation $geneartion should match virtual disk extension $($VhdPath.Split('.')[-1])"
Throw "Generation 2 virtual machines do not support the .VHD virtual disk extension."
}


# Check if $Path exist
if($Path -and !(Test-Path -Path $Path))
{
Expand All @@ -396,6 +398,7 @@ function Test-TargetResource
if($state -and ($vmObj.State -ne $State)){return $false}
if($StartupMemory -and ($vmObj.MemoryStartup -ne $StartupMemory)){return $false}
if($MACAddress -and ($vmObj.NetWorkAdapters.MacAddress -notcontains $MACAddress)){return $false}
if($Generation -ne $vmObj.Generation){return $false}
if($ProcessorCount -and ($vmObj.ProcessorCount -ne $ProcessorCount)){return $false}
if($MaximumMemory -and ($vmObj.MemoryMaximum -ne $MaximumMemory)){return $false}
if($MinimumMemory -and ($vmObj.MemoryMinimum -ne $MinimumMemory)){return $false}
Expand Down Expand Up @@ -510,4 +513,3 @@ function Get-VMIPAddress
#endregion

Export-ModuleMember -Function *-TargetResource

4 changes: 2 additions & 2 deletions DSCResources/MSFT_xVMHyperV/MSFT_xVMHyperV.schema.mof
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[ClassVersion("1.0.0"), FriendlyName("xVMHyperV")]
[ClassVersion("1.0.0"), FriendlyName("xVMHyperV")]
class MSFT_xVMHyperV : OMI_BaseResource
{
[Key, Description("Name of the VM")] String Name;
[Required, Description("VHD associated with the VM")] String VhdPath;
[Write, Description("Virtual switch associated with the VM")] String SwitchName;
[Write, Description("State of the VM."), ValueMap{"Running","Paused","Off"}, Values{"Running","Paused","Off"}] String State;
[Write, Description("Folder where the VM data will be stored")] String Path;
[Write, Description("Associated Virtual disk format - Vhd or Vhdx"), ValueMap{"Vhd","Vhdx"}, Values{"Vhd","Vhdx"}] String Generation;
[Write, Description("Virtual machine generation")] Uint32 String Generation;
[Write, Description("Startup RAM for the VM.")] Uint64 StartupMemory;
[Write, Description("Minimum RAM for the VM. This enables dynamic memory.")] Uint64 MinimumMemory;
[Write, Description("Maximum RAM for the VM. This enable dynamic memory.")] Uint64 MaximumMemory;
Expand Down
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ This resource is particularly useful when bootstrapping DSC Configurations into
* **SwitchName**: Virtual switch associated with the VM
* **State**: State of the VM: { Running | Paused | Off }
* **Path**: Folder where the VM data will be stored
* **Generation**: Associated Virtual disk format { Vhd | Vhdx }
* **Generation**: Virtual machine generation { 1 | 2 }.
Generation 2 virtual machines __only__ support VHDX files.
* **StartupMemory**: Startup RAM for the VM
* **MinimumMemory**: Minimum RAM for the VM.
Setting this property enables dynamic memory.
Expand All @@ -45,6 +46,12 @@ Setting this property enables dynamic memory.
* **RestartIfNeeded**: If specified, will shutdown and restart the VM as needed for property changes
* **Ensure**: Ensures that the VM is Present or Absent

The following xVMHyper-V properties **cannot** be changed after VM creation:

* VhdPath
* Path
* Generation

### xVMSwitch

* **Name**: The desired VM Switch name
Expand All @@ -61,6 +68,12 @@ Please see the Examples section for more details.

## Versions

### Unreleased
* Decoupled VM generation from underlying VHD format in xVMHyperV resource.
* The initial generation property was tied to the virtual disk format which was incorrect and has been rectified.
* __This is a breaking change__ due to the xVMHyperV.Generation property has changing from a String to an Integer.
* However, this change will only impact configurations that have previously explicitly specified the VM generation is either "vhd" or "vhdx".

### 2.4.0.0
* Fixed VM power state issue in xVMHyperV resource

Expand All @@ -70,7 +83,6 @@ Please see the Examples section for more details.

### 2.2.1


### 2.1

* Added logic to automatically adjust VM’s startup memory when only minimum and maximum memory is specified in configuration
Expand All @@ -88,7 +100,6 @@ Please see the Examples section for more details.
- xVMHyperV
- xVMSwitch


## Examples

### End-to-End Example
Expand Down Expand Up @@ -147,7 +158,7 @@ Configuration Sample_EndToEndXHyperV_RunningVM
}
# create the testVM out of the vhd.
# create the generation 1 testVM out of the vhd.
xVMHyperV testvm
{
Name = "$($name)_vm"
Expand Down Expand Up @@ -247,9 +258,9 @@ Configuration Sample_xVhd_DiffVHD
}
```

### Create a VM for a given VHD
### Create a generation 2 VM for a given VHD

This configuration will create a VM, given a VHD, on Hyper-V host.
This configuration will create a VM, given a VHDX, on Hyper-V host.

```powershell
Configuration Sample_xVMHyperV_Simple
Expand All @@ -262,7 +273,7 @@ Configuration Sample_xVMHyperV_Simple
[string]$VMName,
[Parameter(Mandatory)]
[string]$VhdPath
[string]$VhdxPath
)
Import-DscResource -module xHyper-V
Expand All @@ -281,8 +292,8 @@ Configuration Sample_xVMHyperV_Simple
{
Ensure = 'Present'
Name = $VMName
VhdPath = $VhdPath
Generation = $VhdPath.Split('.')[-1]
VhdPath = $VhdxPath
Generation = 2
DependsOn = '[WindowsFeature]HyperV'
}
}
Expand All @@ -306,6 +317,10 @@ Configuration Sample_xVMHyperV_DynamicMemory
[Parameter(Mandatory)]
[string]$VhdPath,
[Parameter(Mandatory)]
[ValidateSet(1,2)]
[unit32]$Generation,
[Parameter(Mandatory)]
[Uint64]$StartupMemory,
Expand Down Expand Up @@ -333,7 +348,7 @@ Configuration Sample_xVMHyperV_DynamicMemory
Ensure = 'Present'
Name = $VMName
VhdPath = $VhdPath
Generation = $VhdPath.Split('.')[-1]
Generation = $Generation
StartupMemory = $StartupMemory
MinimumMemory = $MinimumMemory
MaximumMemory = $MaximumMemory
Expand All @@ -360,6 +375,10 @@ Configuration Sample_xVMHyperV_Complete
[Parameter(Mandatory)]
[string]$VhdPath,
[Parameter(Mandatory)]
[ValidateSet(1,2)]
[unit32]$Generation,
[Parameter(Mandatory)]
[Uint64]$StartupMemory,
Expand Down Expand Up @@ -404,7 +423,7 @@ Configuration Sample_xVMHyperV_Complete
SwitchName = $SwitchName
State = $State
Path = $Path
Generation = $VhdPath.Split('.')[-1]
Generation = $Generation
StartupMemory = $StartupMemory
MinimumMemory = $MinimumMemory
MaximumMemory = $MaximumMemory
Expand Down
68 changes: 56 additions & 12 deletions Tests/MSFT_xVMHyper-V/xVMHyper-V.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,25 @@ Describe 'xVMHyper-V' {
## Create empty functions to be able to mock the missing Hyper-V cmdlets
## CmdletBinding required on Get-VM to support $ErrorActionPreference
function Get-VM { [CmdletBinding()] param( [Parameter(ValueFromRemainingArguments)] $Name) }
function New-VM { }
## Generation parameter is required for the mocking -ParameterFilter to work
function New-VM { param ( $Generation) }
function Set-VM { }
function Stop-VM { }
function Remove-VM { }
function Get-VMNetworkAdapter { }
function Set-VMNetworkAdapter { }

$stubVMDisk = New-Item -Path 'TestDrive:\TestVM.vhdx' -ItemType File;
$stubVhdxDisk = New-Item -Path 'TestDrive:\TestVM.vhdx' -ItemType File;
$stubVhdDisk = New-Item -Path 'TestDrive:\TestVM.vhd' -ItemType File;
$StubVMConfig = New-Item -Path 'TestDrive:\TestVM.xml' -ItemType File;
$stubVM = @{
HardDrives = @(
@{ Path = $stubVMDisk.FullName; }
@{ Path = $stubVhdxDisk.FullName; }
@{ Path = $stubVhdDisk.FullName; }
);
State = 'Running';
#State = 'Running';
Path = $StubVMConfig.FullPath;
Generation = 2;
Generation = 1;
MemoryStartup = 512MB;
MinimumMemory = 128MB;
MaximumMemory = 4096MB;
Expand All @@ -62,18 +65,17 @@ Describe 'xVMHyper-V' {
Context 'Validates Get-TargetResource Method' {

It 'Returns a hashtable' {
$targetResource = Get-TargetResource -Name 'RunningVM' -VhdPath $stubVMDisk.FullName;
$targetResource = Get-TargetResource -Name 'RunningVM' -VhdPath $stubVhdxDisk.FullName;
$targetResource -is [System.Collections.Hashtable] | Should Be $true;
}
It 'Throws when multiple VMs are present' {
{ Get-TargetResource -Name 'DuplicateVM' -VhdPath $stubVMDisk.FullName } | Should Throw;
{ Get-TargetResource -Name 'DuplicateVM' -VhdPath $stubVhdxDisk.FullName } | Should Throw;
}
} #end context Validates Get-TargetResource Method

Context 'Validates Test-TargetResource Method' {
$testParams = @{
VhdPath = $stubVMDisk.FullName;
Generation = 'Vhdx';
VhdPath = $stubVhdxDisk.FullName;
}

It 'Returns a boolean' {
Expand Down Expand Up @@ -129,20 +131,47 @@ Describe 'xVMHyper-V' {
Test-TargetResource -Name 'StoppedVM' -State Running @testParams | Should Be $false;
}

It 'Returns $true when VM .vhd file is specified with a generation 1 VM' {
Test-TargetResource -Name 'StoppedVM' -VhdPath $stubVhdDisk -Generation 1 | Should Be $true;
}

It 'Returns $true when VM .vhdx file is specified with a generation 1 VM' {
Test-TargetResource -Name 'StoppedVM' -VhdPath $stubVhdxDisk -Generation 1 | Should Be $true;
}

It 'Returns $true when VM .vhdx file is specified with a generation 2 VM' {
Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'Generation2VM' } -MockWith {
$generation2VM = $stubVM.Clone();
$generation2VM['Generation'] = 2;
return [PSCustomObject] $generation2VM;
}
Test-TargetResource -Name 'Generation2VM' -VhdPath $stubVhdxDisk -Generation 2 | Should Be $true;
}

It 'Throws when a VM .vhd file is specified with a generation 2 VM' {
{ Test-TargetResource -Name 'Gen2VM' -VhdPath $stubVhdDisk -Generation 2 } | Should Throw;
}

It 'Throws when Hyper-V Tools are not installed' {
## This test needs to be the last in the Context otherwise all subsequent Get-Module checks will fail
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith { }
{ Test-TargetResource -Name 'RunningVM' @testParams } | Should Throw;
}

} #end context Validates Test-TargetResource Method

Context 'Validates Set-TargetResource Method' {
$testParams = @{
VhdPath = $stubVMDisk.FullName;
Generation = 'Vhdx';
VhdPath = $stubVhdxDisk.FullName;
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'NewVM' } -MockWith { }
Mock -CommandName New-VM -MockWith { $newVM = $stubVM.Clone(); $newVM['State'] = 'Off'; return $newVM; }
Mock -CommandName New-VM -MockWith {
$newVM = $stubVM.Clone();
$newVM['State'] = 'Off';
$newVM['Generation'] = $Generation;
return $newVM;
}
Mock -CommandName Set-VM -MockWith { return $true; }
Mock -CommandName Stop-VM -MockWith { return $true; } # requires output to be able to pipe something into Remove-VM
Mock -CommandName Remove-VM -MockWith { return $true; }
Expand Down Expand Up @@ -189,6 +218,21 @@ Describe 'xVMHyper-V' {
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 1 -Scope It;
}

It 'Creates a generation 1 VM by default/when not explicitly specified' {
Set-TargetResource -Name 'NewVM' @testParams;
Assert-MockCalled -CommandName New-VM -ParameterFilter { $Generation -eq 1 } -Scope It;
}

It 'Creates a generation 1 VM when explicitly specified' {
Set-TargetResource -Name 'NewVM' -Generation 1 @testParams;
Assert-MockCalled -CommandName New-VM -ParameterFilter { $Generation -eq 1 } -Scope It;
}

It 'Creates a generation 2 VM when explicitly specified' {
Set-TargetResource -Name 'NewVM' -Generation 2 @testParams;
Assert-MockCalled -CommandName New-VM -ParameterFilter { $Generation -eq 2 } -Scope It;
}

It 'Throws when Hyper-V Tools are not installed' {
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith { }
{ Set-TargetResource -Name 'RunningVM' @testParams } | Should Throw;
Expand Down

0 comments on commit 36e0ec1

Please sign in to comment.