diff --git a/CHANGELOG.md b/CHANGELOG.md index d0db64765..1bf8abc12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ - Changes to xSQLServerSetup - Properly checks for use of SQLSysAdminAccounts parameter in $PSBoundParameters. The test now also properly evaluates the setup argument for SQLSysAdminAccounts. - Enables CodeCov.io code coverage reporting +- Examples + - xSQLServerMaxDop + - 1-SetMaxDopToOne.ps1 + - 2-SetMaxDopToAuto.ps1 + - 3-SetMaxDopToDefault.ps1 +- Added tests for resources + - xSQLServerMaxDop ## 5.0.0.0 diff --git a/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.psm1 b/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.psm1 index 552071f02..06a1a7ca7 100644 --- a/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.psm1 +++ b/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.psm1 @@ -1,200 +1,282 @@ -$currentPath = Split-Path -Parent $MyInvocation.MyCommand.Path -Write-Verbose -Message "CurrentPath: $currentPath" - -# Load Common Code -Import-Module $currentPath\..\..\xSQLServerHelper.psm1 -Verbose:$false -ErrorAction Stop - -function Get-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $SQLInstanceName, - - [System.String] - $SQLServer = $env:COMPUTERNAME - ) - - if(!$sql) - { - $sql = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName - } - - if($sql) - { - $currentMaxDop = $sql.Configuration.MaxDegreeOfParallelism.ConfigValue - if($currentMaxDop) - { - New-VerboseMessage -Message "MaxDop is $currentMaxDop" - } - } - - $returnValue = @{ - SQLInstanceName = $SQLInstanceName - SQLServer = $SQLServer - MaxDop = $currentMaxDop - } - - $returnValue -} - -function Set-TargetResource -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $SQLInstanceName, - - [System.String] - $SQLServer = $env:COMPUTERNAME, - - [ValidateSet("Present","Absent")] - [System.String] - $Ensure = 'Present', - - [System.Boolean] - $DynamicAlloc = $false, - - [System.Int32] - $MaxDop = 0 - ) - - if(!$sql) - { - $sql = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName - } - - if($sql) - { - switch($Ensure) - { - "Present" - { - if($DynamicAlloc -eq $true) - { - $MaxDop = Get-MaxDopDynamic $sql - } - } - - "Absent" - { - $MaxDop = 0 - } - } - - try - { - $sql.Configuration.MaxDegreeOfParallelism.ConfigValue = $MaxDop - $sql.alter() - New-VerboseMessage -Message "Set MaxDop to $MaxDop" - } - catch - { - New-VerboseMessage -Message "Failed setting MaxDop to $MaxDop" - } - } -} - -function Test-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $SQLInstanceName, - - [System.String] - $SQLServer = $env:COMPUTERNAME, - - [ValidateSet("Present","Absent")] - [System.String] - $Ensure = 'Present', - - [System.Boolean] - $DynamicAlloc = $false, - - [System.Int32] - $MaxDop = 0 - ) - - if(!$sql) - { - $sql = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName - } - - $currentMaxDop = $sql.Configuration.MaxDegreeOfParallelism.ConfigValue - - switch($Ensure) - { - "Present" - { - if($DynamicAlloc -eq $true) - { - $MaxDop = Get-MaxDopDynamic $sql - New-VerboseMessage -Message "Dynamic MaxDop is $MaxDop." - } - - if ($currentMaxDop -eq $MaxDop) - { - New-VerboseMessage -Message "Current MaxDop is at Requested value $MaxDop." - return $true - } - else - { - New-VerboseMessage -Message "Current MaxDop is $currentMaxDop should be updated to $MaxDop" - return $false - } - } - - "Absent" - { - if ($currentMaxDop -eq 0) - { - New-VerboseMessage -Message "Current MaxDop is at Requested value 0." - return $true - } - else - { - New-VerboseMessage -Message "Current MaxDop is $currentMaxDop should be updated to 0" - return $false - } - } - } -} - -function Get-MaxDopDynamic -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param( - $Sql - ) - - $numCores = $Sql.Processors - $numProcs = ($Sql.AffinityInfo.NumaNodes | Measure-Object).Count - - if ($numProcs -eq 1) - { - $maxDop = ($numCores / 2) - $maxDop = [Math]::Round($maxDop, [system.midpointrounding]::AwayFromZero) - } - elseif ($numCores -ge 8) - { - $maxDop = 8 - } - else - { - $maxDop = $numCores - } - - $maxDop -} - -Export-ModuleMember -Function *-TargetResource +Import-Module -Name (Join-Path -Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) ` + -ChildPath 'xSQLServerHelper.psm1') ` + -Force +<# + .SYNOPSIS + This function gets the max degree of parallelism server configuration option. + + .PARAMETER SQLServer + The host name of the SQL Server to be configured. + + .PARAMETER SQLInstanceName + The name of the SQL instance to be configured. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $SQLInstanceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $SQLServer = $env:COMPUTERNAME + ) + + $sqlServerObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName + + if ($sqlServerObject) + { + Write-Verbose -Message 'Getting the max degree of parallelism server configuration option' + $currentMaxDop = $sqlServerObject.Configuration.MaxDegreeOfParallelism.ConfigValue + } + + $returnValue = @{ + SQLInstanceName = $SQLInstanceName + SQLServer = $SQLServer + MaxDop = $currentMaxDop + } + + $returnValue +} + +<# + .SYNOPSIS + This function sets the max degree of parallelism server configuration option. + + .PARAMETER SQLServer + The host name of the SQL Server to be configured. + + .PARAMETER SQLInstanceName + The name of the SQL instance to be configured. + + .PARAMETER Ensure + When set to 'Present' then max degree of parallelism will be set to either the value in parameter MaxDop or dynamically configured when parameter DynamicAlloc is set to $true. + When set to 'Absent' max degree of parallelism will be set to 0 which means no limit in number of processors used in parallel plan execution. + + .PARAMETER DynamicAlloc + If set to $true then max degree of parallelism will be dynamically configured. + When this is set parameter is set to $true, the parameter MaxDop must be set to $null or not be configured. + + .PARAMETER MaxDop + A numeric value to limit the number of processors used in parallel plan execution. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $SQLInstanceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $SQLServer = $env:COMPUTERNAME, + + [Parameter()] + [ValidateSet('Present','Absent')] + [ValidateNotNullOrEmpty()] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Boolean] + $DynamicAlloc, + + [Parameter()] + [System.Int32] + $MaxDop + ) + + $sqlServerObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName + + if ($sqlServerObject) + { + Write-Verbose -Message 'Setting the max degree of parallelism server configuration option' + switch ($Ensure) + { + 'Present' + { + if ($DynamicAlloc) + { + if ($MaxDop) + { + throw New-TerminatingError -ErrorType MaxDopParamMustBeNull ` + -FormatArgs @( $SQLServer,$SQLInstanceName ) ` + -ErrorCategory InvalidArgument + } + + $targetMaxDop = Get-SqlDscDynamicMaxDop -SqlServerObject $sqlServerObject + New-VerboseMessage -Message "Dynamic MaxDop is $targetMaxDop." + } + else + { + $targetMaxDop = $MaxDop + } + } + + 'Absent' + { + $targetMaxDop = 0 + New-VerboseMessage -Message 'Desired state should be absent - MAXDOP is reset to the default value.' + } + } + + try + { + $sqlServerObject.Configuration.MaxDegreeOfParallelism.ConfigValue = $targetMaxDop + $sqlServerObject.Alter() + New-VerboseMessage -Message "Setting MAXDOP value to $targetMaxDop." + } + catch + { + throw New-TerminatingError -ErrorType MaxDopSetError ` + -FormatArgs @($SQLServer,$SQLInstanceName,$targetMaxDop) ` + -ErrorCategory InvalidOperation ` + -InnerException $_.Exception + } + } +} + +<# + .SYNOPSIS + This function tests the max degree of parallelism server configuration option. + + .PARAMETER SQLServer + The host name of the SQL Server to be configured. + + .PARAMETER SQLInstanceName + The name of the SQL instance to be configured. + + .PARAMETER Ensure + When set to 'Present' then max degree of parallelism will be set to either the value in parameter MaxDop or dynamically configured when parameter DynamicAlloc is set to $true. + When set to 'Absent' max degree of parallelism will be set to 0 which means no limit in number of processors used in parallel plan execution. + + .PARAMETER DynamicAlloc + If set to $true then max degree of parallelism will be dynamically configured. + When this is set parameter is set to $true, the parameter MaxDop must be set to $null or not be configured. + + .PARAMETER MaxDop + A numeric value to limit the number of processors used in parallel plan execution. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $SQLInstanceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $SQLServer = $env:COMPUTERNAME, + + [Parameter()] + [ValidateSet('Present','Absent')] + [ValidateNotNullOrEmpty()] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Boolean] + $DynamicAlloc, + + [Parameter()] + [System.Int32] + $MaxDop + ) + + Write-Verbose -Message 'Testing the max degree of parallelism server configuration option' + + $parameters = @{ + SQLInstanceName = $PSBoundParameters.SQLInstanceName + SQLServer = $PSBoundParameters.SQLServer + } + + $currentValues = Get-TargetResource @parameters + $getMaxDop = $currentValues.MaxDop + $isMaxDopInDesiredState = $true + + switch ($Ensure) + { + 'Absent' + { + if ($getMaxDop -ne 0) + { + New-VerboseMessage -Message "Current MaxDop is $getMaxDop should be updated to 0" + $isMaxDopInDesiredState = $false + } + } + 'Present' + { + if ($DynamicAlloc) + { + if ($MaxDop) + { + throw New-TerminatingError -ErrorType MaxDopParamMustBeNull ` + -FormatArgs @( $SQLServer,$SQLInstanceName ) ` + -ErrorCategory InvalidArgument + } + + $dynamicMaxDop = Get-SqlDscDynamicMaxDop + New-VerboseMessage -Message "Dynamic MaxDop is $dynamicMaxDop." + + if ($getMaxDop -ne $dynamicMaxDop) + { + New-VerboseMessage -Message "Current MaxDop is $getMaxDop should be updated to $dynamicMaxDop" + $isMaxDopInDesiredState = $false + } + } + else + { + if ($getMaxDop -ne $MaxDop) + { + New-VerboseMessage -Message "Current MaxDop is $getMaxDop should be updated to $MaxDop" + $isMaxDopInDesiredState = $false + } + } + } + } + + $isMaxDopInDesiredState +} + +<# + .SYNOPSIS + This cmdlet is used to return the dynamic max degree of parallelism +#> +function Get-SqlDscDynamicMaxDop +{ + $cimInstanceProc = Get-CimInstance -ClassName Win32_Processor + $numProcs = (Measure-Object -InputObject $cimInstanceProc -Property NumberOfLogicalProcessors -Sum).Sum + $numCores = (Measure-Object -InputObject $cimInstanceProc -Property NumberOfCores -Sum).Sum + + if ($numProcs -eq 1) + { + $dynamicMaxDop = [Math]::Round($numCores / 2, [System.MidpointRounding]::AwayFromZero) + } + elseif ($numCores -ge 8) + { + $dynamicMaxDop = 8 + } + else + { + $dynamicMaxDop = $numCores + } + + $dynamicMaxDop +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.schema.mof b/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.schema.mof index 59f0a8591..0ca9038ac 100644 --- a/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.schema.mof +++ b/DSCResources/MSFT_xSQLServerMaxDop/MSFT_xSQLServerMaxDop.schema.mof @@ -1,11 +1,9 @@ - [ClassVersion("1.0.0.0"), FriendlyName("xSQLServerMaxDop")] class MSFT_xSQLServerMaxDop : OMI_BaseResource { - [Write, Description("An enumerated value that describes if MaxDop is configured"), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Write, Description("An enumerated value that describes if MaxDop is configured (Present) or reset to default value (Absent)"), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Flag to Dynamically allocate Maxdop based on Best Practices")] Boolean DynamicAlloc; [Write, Description("Numeric value to configure Maxdop to")] Sint32 MaxDop; - [Write, Description("SQL Server to configure Maxdop on")] String SQLServer; - [Key, Description("SQL Instance to configure Maxdop on")] String SQLInstanceName; + [Write, Description("The host name of the SQL Server to be configured. Default value is '$env:COMPUTERNAME'.")] String SQLServer; + [Key, Description("The name of the SQL instance to be configured.")] String SQLInstanceName; }; - diff --git a/Examples/Resources/xSQLServerMaxDop/1-SetMaxDopToOne.ps1 b/Examples/Resources/xSQLServerMaxDop/1-SetMaxDopToOne.ps1 new file mode 100644 index 000000000..094f91f50 --- /dev/null +++ b/Examples/Resources/xSQLServerMaxDop/1-SetMaxDopToOne.ps1 @@ -0,0 +1,30 @@ +<# +.EXAMPLE + This example shows how to set max degree of parallelism server + configuration option with the value equal to 1. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $SysAdminAccount + ) + + Import-DscResource -ModuleName xSqlServer + + node localhost + { + xSQLServerMaxDop Set_SQLServerMaxDop_ToOne + { + Ensure = 'Present' + DynamicAlloc = $false + MaxDop = 1 + SQLServer = 'SQLServer' + SQLInstanceName = 'DSC' + PsDscRunAsCredential = $SysAdminAccount + } + } +} diff --git a/Examples/Resources/xSQLServerMaxDop/2-SetMaxDopToAuto.ps1 b/Examples/Resources/xSQLServerMaxDop/2-SetMaxDopToAuto.ps1 new file mode 100644 index 000000000..783f17e31 --- /dev/null +++ b/Examples/Resources/xSQLServerMaxDop/2-SetMaxDopToAuto.ps1 @@ -0,0 +1,29 @@ +<# +.EXAMPLE + This example shows how to set max degree of parallelism server + configuration option with the automatic configuration. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $SysAdminAccount + ) + + Import-DscResource -ModuleName xSqlServer + + node localhost + { + xSQLServerMaxDop Set_SQLServerMaxDop_ToAuto + { + Ensure = 'Present' + DynamicAlloc = $true + SQLServer = 'SQLServer' + SQLInstanceName = 'DSC' + PsDscRunAsCredential = $SysAdminAccount + } + } +} diff --git a/Examples/Resources/xSQLServerMaxDop/3-SetMaxDopToDefault.ps1 b/Examples/Resources/xSQLServerMaxDop/3-SetMaxDopToDefault.ps1 new file mode 100644 index 000000000..0685a0498 --- /dev/null +++ b/Examples/Resources/xSQLServerMaxDop/3-SetMaxDopToDefault.ps1 @@ -0,0 +1,28 @@ +<# +.EXAMPLE + This example shows how to set max degree of parallelism server + configuration option with the default configuration. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $SysAdminAccount + ) + + Import-DscResource -ModuleName xSqlServer + + node localhost + { + xSQLServerMaxDop Set_SQLServerMaxDop_ToDefault + { + Ensure = 'Absent' + SQLServer = 'SQLServer' + SQLInstanceName = 'DSC' + PsDscRunAsCredential = $SysAdminAccount + } + } +} diff --git a/README.md b/README.md index f550be0a7..2093d4e24 100644 --- a/README.md +++ b/README.md @@ -602,7 +602,15 @@ None. ### xSQLServerMaxDop -No description. +This resource set the max degree of parallelism server configuration option. +The max degree of parallelism option is used to limit the number of processors to use in parallel plan execution. +Read more about max degree of parallelism in this article [Configure the max degree of parallelism Server Configuration Option](https://msdn.microsoft.com/en-us/library/ms189094.aspx) + +#### Formula for dynamically allocating max degree of parallelism + +* If the number of configured NUMA nodes configured in SQL Server equals 1, then max degree of parallelism is calculated using number of cores divided in 2 (numberOfCores / 2), then rounded up to the next integer (3.5 > 4). +* If the number of cores configured in SQL Server are greater than or equal to 8 cores then max degree of parallelism will be set to 8. +* If the number of configured NUMA nodes configured in SQL Server is greater than 2 and thenumber of cores are less than 8 then max degree of parallelism will be set to the number of cores. #### Requirements @@ -611,15 +619,17 @@ No description. #### Parameters -* **[String] SQLInstance** (Key): The SQL instance where to set MaxDop -* **[String] Ensure** _(Write)_: An enumerated value that describes if Min and Max memory is configured. { *Present* | Absent }. -* **[Boolean] DyamicAlloc** _(Write)_: Flag to indicate if MaxDop is dynamically configured -* **[Sint32] MaxDop** _(Write)_: Numeric value to configure MaxDop to -* **[String] SQLServer** _(Write)_: The SQL Server where to set MaxDop +* **[String] SQLInstance** (Key): The name of the SQL instance to be configured. +* **[String] SQLServer** _(Write)_: The host name of the SQL Server to be configured. Default value is *env:COMPUTERNAME*. +* **[String] Ensure** _(Write)_: When set to 'Present' then max degree of parallelism will be set to either the value in parameter MaxDop or dynamically configured when parameter DynamicAlloc is set to $true. When set to 'Absent' max degree of parallelism will be set to 0 which means no limit in number of processors used in parallel plan execution. { *Present* | Absent }. +* **[Boolean] DynamicAlloc** _(Write)_: If set to $true then max degree of parallelism will be dynamically configured. When this is set parameter is set to $true, the parameter MaxDop must be set to $null or not be configured. +* **[Sint32] MaxDop** _(Write)_: A numeric value to limit the number of processors used in parallel plan execution. #### Examples -None. +* [Set SQLServerMaxDop to 1](/Examples/Resources/xSQLServerMaxDop/1-SetMaxDopToOne.ps1) +* [Set SQLServerMaxDop to Auto](/Examples/Resources/xSQLServerMaxDop/2-SetMaxDopToAuto.ps1) +* [Set SQLServerMaxDop to Default](/Examples/Resources/xSQLServerMaxDop/3-SetMaxDopToDefault.ps1) ### xSQLServerMemory diff --git a/Tests/Unit/MSFT_xSQLServerMaxDop.Tests.ps1 b/Tests/Unit/MSFT_xSQLServerMaxDop.Tests.ps1 new file mode 100644 index 000000000..10e01e63b --- /dev/null +++ b/Tests/Unit/MSFT_xSQLServerMaxDop.Tests.ps1 @@ -0,0 +1,455 @@ +$script:DSCModuleName = 'xSQLServer' +$script:DSCResourceName = 'MSFT_xSQLServerMaxDop' + +#region HEADER + +# Unit Test Template Version: 1.2.0 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup { +} + +function Invoke-TestCleanup { + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + $mockSQLServerName = 'localhost' + $mockSQLServerInstanceName = 'MSSQLSERVER' + $mockMaxDegreeOfParallelism = 4 + $mockExpectedMaxDopForAlterMethod = 1 + $mockInvalidOperationForAlterMethod = $false + $mockNumberOfLogicalProcessors = 4 + $mockNumberOfCores = 4 + + # Default parameters that are used for the It-blocks + $mockDefaultParameters = @{ + SQLInstanceName = $mockSQLServerInstanceName + SQLServer = $mockSQLServerName + } + + #region Function mocks + + $mockConnectSQL = { + return @( + ( + New-Object Object | + Add-Member -MemberType NoteProperty -Name InstanceName -Value $mockSQLServerInstanceName -PassThru | + Add-Member -MemberType NoteProperty -Name ComputerNamePhysicalNetBIOS -Value $mockSQLServerName -PassThru | + Add-Member -MemberType ScriptProperty -Name Configuration -Value { + return @( ( New-Object Object | + Add-Member -MemberType ScriptProperty -Name MaxDegreeOfParallelism -Value { + return @( ( New-Object Object | + Add-Member -MemberType NoteProperty -Name DisplayName -Value 'max degree of parallelism' -PassThru | + Add-Member -MemberType NoteProperty -Name Description -Value 'maximum degree of parallelism' -PassThru | + Add-Member -MemberType NoteProperty -Name RunValue -Value $mockMaxDegreeOfParallelism -PassThru | + Add-Member -MemberType NoteProperty -Name ConfigValue -Value $mockMaxDegreeOfParallelism -PassThru -Force + ) ) + } -PassThru -Force + ) ) + } -PassThru | + Add-Member -MemberType ScriptMethod -Name Alter -Value { + if ( $this.Configuration.MaxDegreeOfParallelism.ConfigValue -ne $mockExpectedMaxDopForAlterMethod ) + { + throw "Called mocked Alter() method without setting the right MaxDegreeOfParallelism. Expected '{0}'. But was '{1}'." ` + -f $mockExpectedMaxDopForAlterMethod, $this.Configuration.MaxDegreeOfParallelism.ConfigValue + } + if ($mockInvalidOperationForAlterMethod) + { + throw 'Mock Alter Method was called with invalid operation.' + } + } -PassThru -Force + ) + ) + } + + $mockCimInstance_Win32Processor = { + return @( + ( + New-Object Object | + Add-Member -MemberType NoteProperty -Name NumberOfLogicalProcessors -Value $mockNumberOfLogicalProcessors -PassThru | + Add-Member -MemberType NoteProperty -Name NumberOfCores -Value $mockNumberOfCores -PassThru -Force + ) + ) + } + + #endregion + + Describe "MSFT_xSQLServerMaxDop\Get-TargetResource" -Tag 'Get'{ + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + + Context 'When the system is either in the desired state or not in the desired state' { + $testParameters = $mockDefaultParameters + + $result = Get-TargetResource @testParameters + + It 'Should return the current value for MaxDop' { + $result.MaxDop | Should Be $mockMaxDegreeOfParallelism + } + + It 'Should return the same values as passed as parameters' { + $result.SQLServer | Should Be $testParameters.SQLServer + $result.SQLInstanceName | Should Be $testParameters.SQLInstanceName + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Assert-VerifiableMocks + } + + Describe "MSFT_xSQLServerMaxDop\Test-TargetResource" -Tag 'Test'{ + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + + Mock -CommandName Get-CimInstance -MockWith $mockCimInstance_Win32Processor -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Verifiable + + Mock -CommandName Get-CimInstance -MockWith { + throw 'Mocked function Get-CimInstance was called with the wrong set of parameter filters.' + } + } + + Context 'When the system is not in the desired state and DynamicAlloc is set to false' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + MaxDop = 1 + DynamicAlloc = $false + Ensure = 'Present' + } + + It 'Should return the state as false when desired MaxDop is the wrong value' { + $result = Test-TargetResource @testParameters + $result | Should Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should not call the mock function Get-CimInstance' { + Assert-MockCalled Get-CimInstance -Exactly -Times 0 -Scope Context + } + } + + $mockMaxDegreeOfParallelism = 6 + + Context 'When the system is in the desired state and DynamicAlloc is set to false' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + MaxDop = 6 + DynamicAlloc = $false + } + + It 'Should return the state as true when desired MaxDop is the correct value' { + $result = Test-TargetResource @testParameters + $result | Should Be $true + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should not call the mock function Get-CimInstance' { + Assert-MockCalled Get-CimInstance -Exactly -Times 0 -Scope Context + } + } + + $mockMaxDegreeOfParallelism = 4 + + Context 'When the system is in the desired state and DynamicAlloc is set to true' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + DynamicAlloc = $true + } + + It 'Should return the state as true when desired MaxDop is present' { + $result = Test-TargetResource @testParameters + $result | Should Be $true + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { + Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Scope Context + } + } + + $mockNumberOfCores = 2 + + Context 'When the system is not in the desired state, DynamicAlloc is set to true, NumberOfLogicalProcessors = 4 and NumberOfCores = 2' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + DynamicAlloc = $true + } + + It 'Should return the state as false when desired MaxDop is the wrong value' { + $result = Test-TargetResource @testParameters + $result | Should Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { + Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Scope Context + } + } + + $mockNumberOfLogicalProcessors = 1 + + Context 'When the system is not in the desired state, DynamicAlloc is set to true, NumberOfLogicalProcessors = 1 and NumberOfCores = 2' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + DynamicAlloc = $true + } + + It 'Should return the state as false when desired MaxDop is the wrong value' { + $result = Test-TargetResource @testParameters + $result | Should Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { + Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Scope Context + } + } + + $mockNumberOfLogicalProcessors = 4 + $mockNumberOfCores = 8 + + Context 'When the system is not in the desired state, DynamicAlloc is set to true, NumberOfLogicalProcessors = 4 and NumberOfCores = 8' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + DynamicAlloc = $true + } + + It 'Should return the state as false when desired MaxDop is the wrong value' { + $result = Test-TargetResource @testParameters + $result | Should Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { + Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Scope Context + } + } + + Context 'When the system is not in the desired state and Ensure is set to absent' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Ensure = 'Absent' + } + + It 'Should return the state as false when desired MaxDop is the wrong value' { + $result = Test-TargetResource @testParameters + $result | Should Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + $mockMaxDegreeOfParallelism = 0 + + Context 'When the system is in the desired state and Ensure is set to absent' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Ensure = 'Absent' + } + + It 'Should return the state as true when desired MaxDop is the correct value' { + $result = Test-TargetResource @testParameters + $result | Should Be $true + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Context 'When the MaxDop parameter is not null and DynamicAlloc set to true' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + MaxDop = 4 + DynamicAlloc = $true + } + + It 'Should throw the correct error' { + { Test-TargetResource @testParameters } | Should Throw 'MaxDop parameter must be set to $null or not assigned if DynamicAlloc parameter is set to $true.' + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Assert-VerifiableMocks + } + + Describe "MSFT_xSQLServerMaxDop\Set-TargetResource" -Tag 'Set'{ + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + + Mock -CommandName Get-CimInstance -MockWith $mockCimInstance_Win32Processor -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Verifiable + + Mock -CommandName Get-CimInstance -MockWith { + throw 'Mocked function Get-CimInstance was called with the wrong set of parameter filters.' + } + } + + Context 'When the MaxDop parameter is not null and DynamicAlloc set to true' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + MaxDop = 4 + DynamicAlloc = $true + Ensure = 'Present' + } + + It 'Should throw the correct error' { + { Set-TargetResource @testParameters } | Should Throw 'MaxDop parameter must be set to $null or not assigned if DynamicAlloc parameter is set to $true.' + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + $mockMaxDegreeOfParallelism = 0 + $mockExpectedMaxDopForAlterMethod = 0 + + Context 'When the Ensure parameter is set to Absent' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Ensure = 'Absent' + } + + It 'Should Not Throw when Ensure parameter is set to Absent' { + { Set-TargetResource @testParameters } | Should Not Throw + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + $mockMaxDegreeOfParallelism = 1 + $mockExpectedMaxDopForAlterMethod = 1 + + Context 'When the desired MaxDop parameter is not set' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + MaxDop = 1 + DynamicAlloc = $false + Ensure = 'Present' + } + + It 'Should Not Throw when MaxDop parameter is not null and DynamicAlloc set to false' { + { Set-TargetResource @testParameters } | Should Not Throw + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + $mockMaxDegreeOfParallelism = 2 + $mockExpectedMaxDopForAlterMethod = 2 + $mockNumberOfLogicalProcessors = 4 + $mockNumberOfCores = 2 + + Context 'When the system is not in the desired state and DynamicAlloc is set to true' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + DynamicAlloc = $true + Ensure = 'Present' + } + + It 'Should Not Throw when MaxDop parameter is not null and DynamicAlloc set to false' { + { Set-TargetResource @testParameters } | Should Not Throw + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + + It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { + Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + $ClassName -eq 'Win32_Processor' + } -Scope Context + } + } + + $mockInvalidOperationForAlterMethod = $true + + Context 'When the desired MaxDop parameter is not set' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + MaxDop = 1 + DynamicAlloc = $false + Ensure = 'Present' + } + + It 'Shoud throw the correct error when Alter() method was called with invalid operation' { + $throwInvalidOperation = ('Unexpected result when trying to configure the max degree of parallelism ' + ` + 'server configuration option. InnerException: Exception calling "Alter" ' + ` + 'with "0" argument(s): "Mock Alter Method was called with invalid operation."') + + { Set-TargetResource @testParameters } | Should Throw $throwInvalidOperation + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Assert-VerifiableMocks + } + } +} +finally +{ + Invoke-TestCleanup +} diff --git a/en-US/xSQLServer.strings.psd1 b/en-US/xSQLServer.strings.psd1 index ce46b3894..c7f10440a 100644 --- a/en-US/xSQLServer.strings.psd1 +++ b/en-US/xSQLServer.strings.psd1 @@ -72,4 +72,8 @@ RemoveAvailabilityGroupFailed = Failed to remove the availabilty group '{0}' fro # SQLServerHelper ExecuteQueryWithResultsFailed = Executing query with results failed on database '{0}'. ExecuteNonQueryFailed = Executing non-query failed on database '{0}'. + +# Max degree of parallelism +MaxDopSetError = Unexpected result when trying to configure the max degree of parallelism server configuration option. +MaxDopParamMustBeNull = MaxDop parameter must be set to $null or not assigned if DynamicAlloc parameter is set to $true. '@