Skip to content

Commit

Permalink
Merge pull request #8 from iainbrighton/dev
Browse files Browse the repository at this point in the history
Fixes VM Power State Issue
  • Loading branch information
TravisEz13 committed May 23, 2015
2 parents bdee73e + cee8167 commit a4cd9c6
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 9 deletions.
28 changes: 19 additions & 9 deletions DSCResources/MSFT_xVMHyperV/MSFT_xVMHyperV.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ function Set-TargetResource
[String]$SwitchName,

# State of the VM
[AllowNull()]
[ValidateSet("Running","Paused","Off")]
[String]$State = "Off",
[String]$State,

# Folder where the VM data will be stored
[String]$Path,
Expand Down Expand Up @@ -136,8 +137,8 @@ function Set-TargetResource
# One cannot set the VM's vhdpath, path, generation and switchName after creation
else
{
# If the VM is not in right state, set it to right state
if($vmObj.State -ne $State)
# If state has been specified and the VM is not in right state, set it to right state
if($State -and ($vmObj.State -ne $State))
{
Write-Verbose -Message "VM $Name is not $State. Expected $State, actual $($vmObj.State)"
Set-VMState -Name $Name -State $State -WaitForIP $WaitForIP
Expand Down Expand Up @@ -186,8 +187,10 @@ function Set-TargetResource
$changeProperty["ProcessorCount"]=$ProcessorCount
}

# Stop the VM, set the right properties, start the VM
Change-VMProperty -Name $Name -VMCommand "Set-VM" -ChangeProperty $changeProperty -WaitForIP $WaitForIP -RestartIfNeeded $RestartIfNeeded
# Stop the VM, set the right properties, start the VM only if there are properties to change
if ($changeProperty.Count -gt 0) {
Change-VMProperty -Name $Name -VMCommand "Set-VM" -ChangeProperty $changeProperty -WaitForIP $WaitForIP -RestartIfNeeded $RestartIfNeeded
}

# If the VM does not have the right MACAddress, stop the VM, set the right MACAddress, start the VM
if($MACAddress -and ($vmObj.NetWorkAdapters.MacAddress -notcontains $MACAddress))
Expand Down Expand Up @@ -255,9 +258,15 @@ function Set-TargetResource
{
Set-VMNetworkAdapter -VMName $Name -StaticMacAddress $MACAddress
}

Set-VMState -Name $Name -State $State -WaitForIP $WaitForIP
Write-Verbose -Message "VM $Name created and is $State"

Write-Verbose -Message "VM $Name created"

if ($State)
{
Set-VMState -Name $Name -State $State -WaitForIP $WaitForIP
Write-Verbose -Message "VM $Name is $State"
}

}
}
}
Expand All @@ -280,8 +289,9 @@ function Test-TargetResource
[String]$SwitchName,

# State of the VM
[AllowNull()]
[ValidateSet("Running","Paused","Off")]
[String]$State = "Off",
[String]$State,

# Folder where the VM data will be stored
[String]$Path,
Expand Down
198 changes: 198 additions & 0 deletions Tests/MSFT_xVMHyper-V/xVMHyper-V.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
[CmdletBinding()]
param()

if (!$PSScriptRoot) # $PSScriptRoot is not defined in 2.0
{
$PSScriptRoot = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path)
}

$ErrorActionPreference = 'stop'
Set-StrictMode -Version latest

$RepoRoot = (Resolve-Path $PSScriptRoot\..\..).Path

$ModuleName = 'MSFT_xVMHyperV'
Import-Module (Join-Path $RepoRoot "DSCResources\$ModuleName\$ModuleName.psm1") -Force;

Describe 'xVMHyper-V' {
InModuleScope $ModuleName {

## 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 { }
function Set-VM { }
function Stop-VM { }
function Remove-VM { }
function Get-VMNetworkAdapter { }
function Set-VMNetworkAdapter { }

$stubVMDisk = New-Item -Path 'TestDrive:\TestVM.vhdx' -ItemType File;
$StubVMConfig = New-Item -Path 'TestDrive:\TestVM.xml' -ItemType File;
$stubVM = @{
HardDrives = @(
@{ Path = $stubVMDisk.FullName; }
);
State = 'Running';
Path = $StubVMConfig.FullPath;
Generation = 2;
MemoryStartup = 512MB;
MinimumMemory = 128MB;
MaximumMemory = 4096MB;
ProcessorCount = 1;
ID = [System.Guid]::NewGuid().ToString();
CPUUsage = 10;
MemoryAssigned = 512MB;
Uptime = New-TimeSpan -Hours 12;
CreationTime = (Get-Date).AddHours(-12);
DynamicMemoryEnabled = $true;
NetworkAdapters = @(
@{ SwitchName = 'TestSwitch'; MacAddress = 'AA-BB-CC-DD-EE-FF'; IpAddresses = @('192.168.0.1','10.0.0.1'); };
);
Notes = '';
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'RunningVM' } -MockWith { $runningVM = $stubVM.Clone(); $runningVM['State'] = 'Running'; return [PSCustomObject] $runningVM; }
Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'StoppedVM' } -MockWith { $stoppedVM = $stubVM.Clone(); $stoppedVM['State'] = 'Off'; return [PSCustomObject] $stoppedVM; }
Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'PausedVM' } -MockWith { $pausedVM = $stubVM.Clone(); $pausedVM['State'] = 'Paused'; return [PSCustomObject] $pausedVM; }
Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'NonexistentVM' } -MockWith { Write-Error 'VM not found.'; }
Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'DuplicateVM' } -MockWith { return @([PSCustomObject] $stubVM, [PSCustomObject] $stubVM); }
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith { return $true; }

Context 'Validates Get-TargetResource Method' {

It 'Returns a hashtable' {
$targetResource = Get-TargetResource -Name 'RunningVM' -VhdPath $stubVMDisk.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;
}
} #end context Validates Get-TargetResource Method

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

It 'Returns a boolean' {
$targetResource = Test-TargetResource -Name 'RunningVM' @testParams;
$targetResource -is [System.Boolean] | Should Be $true;
}

It 'Returns $true when VM is present and "Ensure" = "Present"' {
Test-TargetResource -Name 'RunningVM' @testParams | Should Be $true;
}

It 'Returns $false when VM is not present and "Ensure" = "Present"' {
Test-TargetResource -Name 'NonexistentVM' @testParams | Should Be $false;
}

It 'Returns $true when VM is not present and "Ensure" = "Absent"' {
Test-TargetResource -Name 'NonexistentVM' -Ensure Absent @testParams | Should Be $true;
}

It 'Returns $false when VM is present and "Ensure" = "Absent"' {
Test-TargetResource -Name 'RunningVM' -Ensure Absent @testParams | Should Be $false;
}

It 'Returns $true when VM is in the "Running" state and no state is explicitly specified' {
Test-TargetResource -Name 'RunningVM' @testParams | Should Be $true;
}

It 'Returns $true when VM is in the "Stopped" state and no state is explicitly specified' {
Test-TargetResource -Name 'StoppedVM' @testParams | Should Be $true;
}

It 'Returns $true when VM is in the "Paused" state and no state is explicitly specified' {
Test-TargetResource -Name 'PausedVM' @testParams | Should Be $true;
}

It 'Returns $true when VM is in the "Running" state and requested "State" = "Running"' {
Test-TargetResource -Name 'RunningVM' @testParams | Should Be $true;
}

It 'Returns $true when VM is in the "Off" state and requested "State" = "Off"' {
Test-TargetResource -Name 'StoppedVM' -State Off @testParams | Should Be $true;
}

It 'Returns $true when VM is in the "Paused" state and requested "State" = Paused"' {
Test-TargetResource -Name 'PausedVM' -State Paused @testParams | Should Be $true;
}

It 'Returns $false when VM is in the "Running" state and requested "State" = "Off"' {
Test-TargetResource -Name 'RunningVM' -State Off @testParams | Should Be $false;
}

It 'Returns $false when VM is in the "Off" state and requested "State" = "Runnning"' {
Test-TargetResource -Name 'StoppedVM' -State Running @testParams | Should Be $false;
}

It 'Throws when Hyper-V Tools are not installed' {
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';
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'NewVM' } -MockWith { }
Mock -CommandName New-VM -MockWith { $newVM = $stubVM.Clone(); $newVM['State'] = 'Off'; 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; }
Mock -CommandName Set-VMNetworkAdapter -MockWith { return $true; }
Mock -CommandName Get-VMNetworkAdapter -MockWith { return $stubVM.NetworkAdapters.IpAddresses; }
Mock -CommandName Set-VMState -MockWith { return $true; }

It 'Removes an existing VM when "Ensure" = "Absent"' {
Set-TargetResource -Name 'RunningVM' -Ensure Absent @testParams;
Assert-MockCalled -CommandName Remove-VM -Scope It;
}

It 'Creates and does not start a VM that does not exist when "Ensure" = "Present"' {
Set-TargetResource -Name 'NewVM' @testParams;
Assert-MockCalled -CommandName New-VM -Exactly -Times 1 -Scope It;
Assert-MockCalled -CommandName Set-VM -Exactly -Times 1 -Scope It;
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 0 -Scope It;
}

It 'Creates and starts a VM that does not exist when "Ensure" = "Present" and "State" = "Running"' {
Set-TargetResource -Name 'NewVM' -State Running @testParams;
Assert-MockCalled -CommandName New-VM -Exactly -Times 1 -Scope It;
Assert-MockCalled -CommandName Set-VM -Exactly -Times 1 -Scope It;
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 1 -Scope It;
}

It 'Does not change VM state when VM "State" = "Running" and requested "State" = "Running"' {
Set-TargetResource -Name 'RunningVM' -State Running @testParams;
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 0 -Scope It;
}

It 'Does not change VM state when VM "State" = "Off" and requested "State" = "Off"' {
Set-TargetResource -Name 'StoppedVM' -State Off @testParams;
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 0 -Scope It;
}

It 'Changes VM state when existing VM "State" = "Off" and requested "State" = "Running"' {
Set-TargetResource -Name 'StoppedVM' -State Running @testParams;
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 1 -Scope It;
}

It 'Changes VM state when existing VM "State" = "Running" and requested "State" = "Off"' {
Set-TargetResource -Name 'RunningVM' -State Off @testParams;
Assert-MockCalled -CommandName Set-VMState -Exactly -Times 1 -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;
}
} #end context Validates Set-TargetResource Method
} #end inmodulescope
} #end describe xVMHyper-V

0 comments on commit a4cd9c6

Please sign in to comment.