Dynamic Memory: Resources from some recent Presentations

WS08R2-HyperV_h_rgb

Over the last few weeks I’ve been travelling around talking about Dynamic Memory in Windows Server 2008 R2 SP1. The sessions were great and as always I got lots of great questions and feedback. One of the most common ones of course is can you share the scripts and the slides. Well of course! I’ll attached the scripts to this post along with the slides as well. They are at the end of this post if you don’t want to read through the rest. Below is the main part of the demo I did and the code used for it. This is probably the first time I’ve put scripts or code directly into a blog post! But before I do that I have to thank Ben Armstrong for his great work on the scripts. They really illustrate Dynamic Memory very well.

Demo Setup

The whole idea of the demo is show how with Dynamic Memory you can increase density on your Hyper-V hosts but still deliver the performance for your workloads. To set this demo up I had to build 16 virtual machines all running Windows 7 with SP1 applied.

dm2

Each of these machines are the exact same image and are running a script called VDIlite which simulates a knowledge worker’s daily tasks of opening documents, websites and waiting a random amount of time between each task. Below is what the code looks like.

VDILite.ps1

  
 # Setup Arrays
$documentSourceArray = @()
$processArray = New-Object System.Collections.ArrayList

# Populate source array.  Entries have three values - the document, the program to open it with, and the process name for closing the process
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\05-2008ClabbySixReasonsWhyMSwillovertakeVMW.PDF""","""C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe""", "AcroRd32")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\BTS09HyperV.pdf""","""C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe""", "AcroRd32")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Hyper-V_ProductOverview_v1_2.pdf""","""C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe""", "AcroRd32")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\LefthandHyper-VBrief.pdf""","""C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe""", "AcroRd32")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Top Ten Reasons for Hyper-V.pdf""","""C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe""", "AcroRd32")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\LiveMigrationWhitepaper_Final.docx""","""C:\Program Files\Microsoft Office\Office14\WINWORD.EXE""", "WINWORD")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows Server 2008 R2 Hyper-V RC Public  FAQ.docx""","""C:\Program Files\Microsoft Office\Office14\WINWORD.EXE""", "WINWORD")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows_Server_2008_R2_Reviewers_Guide_RTM.docx""","""C:\Program Files\Microsoft Office\Office14\WINWORD.EXE""", "WINWORD")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows_Server_2008_R2_TDM_Whitepaper_RTM.docx""","""C:\Program Files\Microsoft Office\Office14\WINWORD.EXE""", "WINWORD")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\WS08_R2_VHD_Performance_WhitePaper_Final.docx""","""C:\Program Files\Microsoft Office\Office14\WINWORD.EXE""", "WINWORD")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Storage and Hyper-V - The Choices You Can Make and the Things You Need to Know.pptx""","""C:\Program Files\Microsoft Office\Office14\POWERPNT.EXE""", "POWERPNT")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows Server 2008 R2 - Hyper-V Part 1.pptx""","""C:\Program Files\Microsoft Office\Office14\POWERPNT.EXE""", "POWERPNT")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows Server 2008 R2 - Hyper-V Part 2.pptx""","""C:\Program Files\Microsoft Office\Office14\POWERPNT.EXE""", "POWERPNT")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\WS08 R2 Hyper-V Overview_R2.pptx""","""C:\Program Files\Microsoft Office\Office14\POWERPNT.EXE""", "POWERPNT")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\WS08 R2 Hyper-V Technical Overview_R2.pptx""","""C:\Program Files\Microsoft Office\Office14\POWERPNT.EXE""", "POWERPNT")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Hyper-V RAM Calculator.xls""","""C:\Program Files\Microsoft Office\Office14\EXCEL.EXE""", "EXCEL")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Appendix_A_-_Test_Results_Spreadsheet.xlsx""","""C:\Program Files\Microsoft Office\Office14\EXCEL.EXE""", "EXCEL")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\DPM 2010 Storage Calculator for Hyper-V DRAFT.xlsx""","""C:\Program Files\Microsoft Office\Office14\EXCEL.EXE""", "EXCEL")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows Server 2008 Hyper-V Attack Surface Reference.xlsx""","""C:\Program Files\Microsoft Office\Office14\EXCEL.EXE""", "EXCEL")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\WinHEC08_Agenda.xlsx""","""C:\Program Files\Microsoft Office\Office14\EXCEL.EXE""", "EXCEL")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Microsoft Events  TechEd USA.mht""","""C:\Program Files\Internet Explorer\iexplore.exe""", "iexplore")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Virtual PC Guy's WebLog - Site Home - MSDN Blogs.mht""","""C:\Program Files\Internet Explorer\iexplore.exe""", "iexplore")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows Server 2008 R2 Virtualization with Hyper-V.mht""","""C:\Program Files\Internet Explorer\iexplore.exe""", "iexplore")
$documentSourceArray += ,("""C:\Users\DM-VDI-Demo\Documents\Windows Virtual PC Home Page.mht""","""C:\Program Files\Internet Explorer\iexplore.exe""", "iexplore")

# Loop for all eternity
while (1 -eq 1)
{

# Generate random sleep time between 10 and 90 seconds
$sleepTime = Get-Random -min 10 -max 90

# Generate random boolean value
$test = [boolean](get-random -input 0,1)

# If the boolean is true - or no processes are running - start a new process
if ($test -or ($processArray.count -eq 0))
   {
   # Choose a random entry from the source array
   $entry = $documentSourceArray[(Get-Random -min 0 -max ($documentSourceArray.length - 1))]
   # Start the process
   $p = [diagnostics.process]::start($entry[1], $entry[0])
   # Add the process name to the process array - if it is not already there
   if (!$processArray.contains($entry[2]))
      {
      [void]$processArray.add($entry[2])
      }
   
   # Display debug information
   write-host "Opening program" $entry[2]
   write-host "Opening file" $entry[0]
   Write-host
   write-host "Numer of open programs:" $processArray.count
   write-host "Open programs:"
   write-host $processArray
   Write-host   
   
   }
# Otherwise close a program
else 
   {
   # Choose a random program, or if there is only one choose it and set the sleep counter to 1 second
   if ($processArray.count -gt 1) {$index = Get-Random -min 0 -max ($processArray.count - 1)}
   else {$index = 0;$sleepTime = 1}
   
   # Display debug information
   write-host "Closing program" $processArray[$index]
   Write-host
   
   # Close the process.  Key things - CloseMainWindow ensures that the process exists politely.
   # All the looping makes sure that all instances are fully closed
   $p = @(Get-Process -name $processArray[$index])
   while ($p = @(Get-Process -name $processArray[$index] -ea 0)) {foreach ($q in $p) {[void]$q.CloseMainWindow()}; sleep 1}
   
   # Remove the process from the array
   $processArray.Remove($processArray[$index])
   
   # Display debug information
   write-host "Numer of remaining programs:" $processArray.count
   write-host "Remaining programs:"
   write-host $processArray
   Write-host
   }

# wait for a random amount of time
write-host "Waiting for" $sleepTime "seconds"
Write-host
sleep $sleepTime  
}

The next step is to make sure each machine has the script running in the startup group so it runs at each logon. I also setup each machine to auto logon so I don’t have to type passwords. I use the command “Control.exe usercontrolpasswords2” Below are some screenshots of how this is configured on each machine.

dm3

dm4

dm5

dm6

The next step in this demo setup is to create each of the Snapshots for Dynamic memory disabled and Dynamic Memory Enabled.  After this part is done the demo flow is fully automate by using the following script.  This script runs on the Hyper-V host where the images are running.

DMDemo.ps1

 # Self Elevate
$wid=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$prp=new-object System.Security.Principal.WindowsPrincipal($wid)
$adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator
$IsAdmin=$prp.IsInRole($adm)
if ($IsAdmin -eq $false)
   {
   $psi = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
   $psi.Arguments = $myInvocation.MyCommand.Definition;
   $psi.Verb = "runas";
   [System.Diagnostics.Process]::Start($psi);
   exit
   }
else
   {
   $Host.UI.RawUI.WindowTitle = "DM Demo (Elevated)"
   $Host.UI.RawUI.BackgroundColor = "DarkBlue"
   clear-host
   }

# Function for handling WMI jobs / return values
Function ProcessResult($localResult, $successString, $failureString)
   {
   #Return success if the return value is "0"
   if ($localResult.ReturnValue -eq 0)
      {write-host $successString} 
 
   #If the return value is not "0" or "4096" then the operation failed
   ElseIf ($localResult.ReturnValue -ne 4096)
      {write-host $failureString "  Error value:" $localResult.ReturnValue}
 
   Else
      {#Get the job object
      $job=[WMI]$localResult.job
 
      #Provide updates if the jobstate is "3" (starting) or "4" (running)
      while ($job.JobState -eq 3 -or $job.JobState -eq 4)
         {#write-host $job.PercentComplete "% complete"
          sleep 1
 
          #Refresh the job object
          $job=[WMI]$localResult.job}
 
       #A jobstate of "7" means success
       if ($job.JobState -eq 7)
          {write-host $successString}
       Else
          {write-host $failureString
          write-host "ErrorCode:" $job.ErrorCode
          write-host "ErrorDescription" $job.ErrorDescription}
       }
   }

# Do a natural bubble sort on a collection of virtual machines
function NaturalBubbleSortVMs ($VMsToSort) 
   {
   $length = $VMsToSort.Length
   $hasChanged = $true
   while ($hasChanged) 
      {
      $hasChanged = $false
      $length--
      for ($i = 0; $i -lt $length; $i++) 
         {
         # Regex used to tell numbers apart from characters
         $regex = "([0-9]+)"
          
         # Split the VM Element name by numbers (the above regex)
         $SplitArray1 = [regex]::split($VMsToSort[$i].ElementName, $regex)
         $SplitArray2 = [regex]::split($VMsToSort[$i+1].ElementName, $regex)
           
         # Use compare-object to find the first chunk of the VM name that does not match
         $CompareObject1 = compare-object $SplitArray1 $SplitArray2 | ?{$_.SideIndicator -eq "=>"} | select -first 1
         $CompareObject2 = compare-object $SplitArray1 $SplitArray2 | ?{$_.SideIndicator -eq "<="} | select -first 1
           
         # $CompareObject1 will be null if both VM names are identical
         if ($CompareObject1) 
            {
            # Get the actual split chunk
            $CompareItem1 = $CompareObject1.InputObject
            $CompareItem2 = $CompareObject2.InputObject
          
            # Check to see if the chunk is a number - if it is, cast it into an integer
            if ($CompareItem1 -match $regex) {$CompareItem1 = [int]$CompareItem1}
            if ($CompareItem2 -match $regex) {$CompareItem2 = [int]$CompareItem2}
           
            # Now strings will be compared as strings, numbers as numbers
            if ($CompareItem2 -gt $CompareItem1) 
               {
               $VMsToSort[$i], $VMsToSort[$i+1] = $VMsToSort[$i+1], $VMsToSort[$i]
               $hasChanged = $true
               }
            }
         }
      } 
   }

# Setup parameters for main menu prompt
$message = "What do you want to do?"
$DMDisabled = New-Object System.Management.Automation.Host.ChoiceDescription "Setup DM &Disabled", "Set the system up for the DM disabled demonstration."
$DMEnabled = New-Object System.Management.Automation.Host.ChoiceDescription "Setup DM &Enabled", "Set the system up for the DM enabled demonstration."
$Base = New-Object System.Management.Automation.Host.ChoiceDescription "Return to &Base configuration", "Revert the system to the base configuration."
$quit = New-Object System.Management.Automation.Host.ChoiceDescription "&Quit", "Exit the DM demo script."
$options = [System.Management.Automation.Host.ChoiceDescription[]]($DMDisabled, $DMEnabled, $Base, $quit)

do
   {

   # Ask the user what they want to do
   $promptResult = $host.ui.PromptForChoice($null, $message, $options, 0) 
   write-host
 
   switch ($promptResult)
      {
         0
            {
            $snapshotName = "DM Disabled"
            $NoVMsToStart = 7
         
            if ((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization").memoryreserve -eq $null)
               {
               New-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization" -name memoryreserve -propertytype DWORD -value 500
               restart-service vmms
               sleep -s 5
               }
            elseif ((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization").memoryreserve -ne 500)
               {
               Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization" -name memoryreserve -value 500
               restart-service vmms
               sleep -s 5
               }
            }
         1
            {
            $snapshotName = "DM Enabled"
            $NoVMsToStart = 12
         
            if ((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization").memoryreserve -ne $null)
               {
               Remove-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization" -name memoryreserve
               restart-service vmms
               sleep -s 5
               }
         
            }
         2
            {
            $snapshotName = "Base"
            $NoVMsToStart = 0
         
            if ((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization").memoryreserve -ne $null)
               {
               Remove-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization" -name memoryreserve
               restart-service vmms
               sleep -s 5
               }
            }
         3
            {
            Exit
            }
      }
   
   # Get the management service
   $VMMS = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService

   # Get the virtual machine objects
   $VMs = @(gwmi MSVM_ComputerSystem -namespace "root\virtualization" | where {$_.ElementName -match "Windows 7"})
   NaturalBubbleSortVMs $VMs

   # Stop any virtual machines that are currently running
   if ($VMs.length -ne 0)
      {
      # results will be stored in an array
      $resultArray1 = @()
   
      # go through all of the virtual machines
      foreach ($VM in $VMs)
         {
         # enabledstate will be "3" if the VM is already off
         if ($VM.EnabledState -ne 3)
            {
            $resultArray1 += $VM.RequestStateChange(3)
            }
         }
      
      # go through the result array and process each of the results
      if ($resultArray1.length -ne 0) 
         { 
         foreach ($result1 in $resultArray1) 
            {ProcessResult $result1 "Stopped Virtual Machine" "Failed to stop virtual machine"}
         }
      }
   
   # Apply the desired snapshot on each virtual machine   
   if ($VMs.length -ne 0)
      {
      # results will be stored in an array
      $resultArray2 = @()
   
      # go through all of the virtual machines
      foreach ($VM in $VMs)
         {
         # Can't use "getrelated" as a snapshot may have multiple associations back to the virtual machine
         # e.g. "LastAppliedSnapshot".  In this case "getrelated" will return an array.  By using a query
         # where the association class is specified we are guaranteed to get a single result
         $query = "Associators of {$VM} Where AssocClass=Msvm_ElementSettingData"
         $VMSnapshot = gwmi -namespace "root\virtualization" -Query $query | where {$_.ElementName -eq $snapshotName}
      
         # Apply the snapshot and store the result in the result array
         $resultArray2 +=  $vmms.ApplyVirtualSystemSnapShotEx($VM,$VMSnapshot)
         }
      
      # go through the result array and process each of the results
      if ($resultArray2.length -ne 0) 
         {
         foreach ($result2 in $resultArray2) 
            {ProcessResult $result2 "Applied snapshot" "Failed to apply snapshot"}
         }
      }
   
   # Start the desired number of virtual machines
   if ($VMs.length -gt ($NoVMsToStart-1))
      {
      # results will be stored in an array
      $resultArray3 = @()
   
      # Start the first "i" virtual machines
      for ([int]$i=0; $i -le ($NoVMsToStart-1); $i++)
         {
         $resultArray3 += $VMs.Get($i).RequestStateChange(2)
         }
      
      # go through the result array and process each of the results
      if ($resultArray3.length -ne 0) 
         {
         foreach ($result3 in $resultArray3) 
            {ProcessResult $result3 "Started Virtual Machine" "Failed to start virtual machine"}
         }
      }
   }
until ($promptResult -eq 3)

So that’s about it. The way the demo is setup it’s very easy to repeat the whole process and takes a couple of minutes to setup before I have to present so it’s a great re-usable demo. So hopefully this is helpful to you out there. The slides and scripts are below for you to download as needed.

Demo Scripts Scripts
PowerPoint Deck Slides

Jeffa

Technorati Tags: Dynamic Memory,Hyper-V R2

Digg This