Wednesday, December 2, 2009 10:49 PM Central Time
Posted by Justin

Compellent Enterprise Manager works great for managing your Storage Center environment and providing reports on volume usage and utilization.

I was looking for a little different spin on the information.  I was looking for a cumulative volume count across an entire Storage Center, plus a total count of replays on the system, and how many of the volumes that exist are actually mapped up to a server object.

For example, the test system that I ran my script on determined that we had over 900 volumes with over 3,000 replays.  We also realized that we had some cleanup to do when we figured out that only 180 of the volumes were actually mapped up.

I did build into the script to collect the page count of each replay so you could tell how large they were if you wanted to; just the calculation needs to be added.

# NAME: VolumeInfo.ps1
# DESC: PowerShell script to report on volume information
# BY  : Justin Braun, Compellent Technologies, Inc.
# DATE: December 1, 2009
# VER : 1.0
#
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND.  THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#
# NOTE: This script assumes a default Get-SCConnection already exists.
 
# Collection
$colVolumes = @()
 
foreach($volume in Get-SCVolume)
{
    Write-Host "Gathering volume data for $volume.Name..."
    
    $replays = $null
    $mappings = $null
    $pagecount = 0
    
    # Volume Information
    $ReportData = New-Object System.Object
    $ReportData | Add-Member -Type NoteProperty -Name "Volume Index" -Value $volume.Index
    $ReportData | Add-Member -Type NoteProperty -Name "Volume Name" -Value $volume.Name
    $ReportData | Add-Member -Type NoteProperty -Name "Volume Size" -Value $volume.Size
    $ReportData | Add-Member -Type NoteProperty -Name "Block Count" -Value $volume.BlockCount
    $ReportData | Add-Member -Type NoteProperty -Name "Created By" -Value $volume.CreateUser
    $ReportData | Add-Member -Type NoteProperty -Name "Created On" -Value $volume.CreateTime
    $ReportData | Add-Member -Type NoteProperty -Name "Modified By" -Value $volume.ModifyUser
    $ReportData | Add-Member -Type NoteProperty -Name "Modified On" -Value $volume.ModifyTime
    $ReportData | Add-Member -Type NoteProperty -Name "Folder" -Value $volume.ParentFolder
    
    # Replay Count Information
    Write-Host "Gathering replay information for $volume.Name..."
    $replays = Get-SCReplay -SourceVolumeIndex $volume.Index
    $ReportData | Add-Member -Type NoteProperty -Name "Replay Count" -Value $replays.Count
    
    # Replay Cumulative Page Information
    Write-Host "Gathering replay page count information for $volume.Name..."
    foreach($replay in $replays)
    {
        $pagecount += $replay.OwnedPageCount
    }
    
    $ReportData | Add-Member -Type NoteProperty -Name "Total Replay Pages" -Value $pagecount
    
    # Volume Mapping Information
    Write-Host "Gathering volume mapping information for $volume.Name..."
    $mappings = Get-SCVolumeMap -VolumeIndex $volume.Index
    
    if($mappings -eq $null)
    {
        $ReportData | Add-Member -Type NoteProperty -Name "Mappings" -Value "No"
    }
    else
    {
        $ReportData | Add-Member -Type NoteProperty -Name "Mappings" -Value "Yes"
    }
    
    # Add to collection
    $colVolumes += $ReportData
    
}
 
# Outfile ReportData Contents
Write-Host "Writing output file..."
 
$colVolumes | export-csv -path "c:\volumeinfo.txt"
 
Write-Host "Done!"

If you have any ideas on how this script could be more useful in your environment, drop me a comment below.

Wednesday, December 2, 2009 3:02 PM Central Time
Posted by Justin

I’ve been doing lots of work in the lab lately with Exchange 2010 to understand all the new changes and how it works with the Compellent Storage Center.

With Exchange 2010, the concept of Storage Groups no longer exists.  Databases are the sole object and are a peer to the server now.  Database names must be unique, but can be moved from server to server as necessary.

In the past, I’ve shared some scripts on how to provision storage for an Exchange 2007 environment.  I’ve slightly reworked this script to account for no longer needing storage groups, but to also automatically create the mailbox database on the Exchange Server and mount it when completed.

# NAME: Exchange2010LabCreate.ps1
# DESC: PowerShell script to create and map volumes for Exchange 2010 Lab
# BY  : Justin Braun, Compellent Technologies, Inc.
# DATE: November 24, 2009
# VER : 1.0
#
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND.  THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#
 
####################################################
# ERROR HANDLING
####################################################
    #"SilentlyContinue": do not print, continue 
    #"Continue": Print, continue (this is the default) 
    #"Stop": Halt the command or script 
    #"Inquire": Ask the user what to do 
    $ErrorActionPreference = "Inquire"
 
####################################################
# STORAGE CENTER CONFIGURATION INFORMATION
####################################################
    $schost = "storagecenter.lab.test"
    $user = "username"
    $pass = "password"
 
####################################################
# EXCHANGE STORAGE CONFIGURATION
####################################################    
    # Number of Databases Per Server
    $dbsize = "1TB"
    $dbtotal = 1
    $dbDiskFolder = "Assigned"
 
####################################################
# SERVER INFORMATION
####################################################
    # Server to Map To (server defintion on CSC must match server name in Windows because of VDS)
    $ServerName = "E2K10MBX01"
 
####################################################
# MISC. CONFIGURATION SETTINGS
####################################################
    # Volume folder name
    $SCParentFolderName = "E2K10MBX01"
    
    # Use custom disk folders for each volume? (if $false, then a single disk folder config is assumed)
    $useCustomDiskFolders = $false
    
    #Mountpoints (set to $true if mountpoint volumes weren't previously created.  Mountpoint volumes will be 1G by default)
    $createMountpointRoot = $true
    
    # Mountpoint Root
    $dbmproot = "M:\Exchange"
    $dbRootDrive = "M:"
 
##########################################################################################
##########################################################################################
# DO NOT EDIT BELOW THIS LINE !
##########################################################################################
##########################################################################################
 
 
# Creates volume with specified name and size using connection instantiated on script launch
function CreateVolume
{
    param
    (
        [string]     $VolumeName,
        [string]     $VolumeSize,
        [string]     $MPRoot,
        [string]     $SCDiskFolder,
        [bool]         $IsMountPoint
    )
 
    Write-Output "Creating new volume: $volumename..."
    if($useCustomDiskFolders -eq $true)
    {$scvolume = New-SCVolume -Name $VolumeName -Size $VolumeSize -ParentFolder $SCParentFolderName -StorageType $SCDiskFolder;}
    else
    {$scvolume = New-SCVolume -Name $VolumeName -Size $VolumeSize -ParentFolder $SCParentFolderName;}
 
    # Maps volume previously created and returned from CreateVolume function
    Write-Output "Mapping new volume $volumename to $servername..."
    
    # Map Volume (if multiple HBA ports are server will be used, make sure that MPIO is installed on server and remove -SinglePath switch from next line
    New-SCVolumeMap -VolumeIndex $scvolume.Index -ServerIndex $scserver.Index -SinglePath
 
    # Rescan Server
    Write-Output "Rescanning server for new volume..."
    Rescan-DiskDevice -Server $ServerName -RescanDelay 5
 
    # Issue Drive Letter / Mount Point
    Write-Output "Creating access path for new volume..."
    $device = Get-DiskDevice -SerialNumber $scvolume.SerialNumber
    
    # Check to see if the device is there yet after initial rescan
    if($device -eq $null)
    {
        # Device is still null, so let's perform up to 10 rescans before we move on
        $scancount = 0
        
        do
        {
            # Rescan the disk
            Write-Output "Rescanning server for new volume..."
            Rescan-DiskDevice -Server $ServerName -RescanDelay 5
            $scancount ++
            
            # Try getting the device again
            $device = Get-DiskDevice -SerialNumber $scvolume.SerialNumber
        }
        until($device -ne $null -or $scancount -eq 10)
    }
    
    # Set variable (this is only used if this is a drive letter mount)
    $finalpath = $MPRoot
    
    # Set full mountpoint path (create path if it doesn't exist)
    if($IsMountPoint -eq $true)
    {
        $finalpath = "$MPRoot\$VolumeName"
        
        # Check to make sure the full mountpoint path acutually exists, otherwise create it
        if (!(Test-Path -path "$finalpath\"))
        {
            New-Item "$finalpath\" -type directory
        }
    }
    
    Write-Output "Onlining Disk and setting access path to $finalpath..."
 
    # Finish creation of mountpoint/drive access
    Set-DiskDevice -SerialNumber $device.SerialNumber -Online
    Set-DiskDevice -SerialNumber $device.SerialNumber -ReadOnly:$false 
    $newvol = New-Volume -DeviceName $device.DeviceName -Server $ServerName -Label $VolumeName -AccessPath $finalpath
    
    # Null out device
    $device = $null
}
 
function LoadSnapins
{
    # Load Exchange Management Shell & Compellent Storage Center Snapins (if not already)
     $LoadedSnapins = Get-PSSnapin;
    $SnapinsToLoad = "Compellent.StorageCenter.Scripting", "Microsoft.Exchange.Management.PowerShell.E2010"
    
    "Adding PowerShell Snapins..."
 
    foreach($snapin in $SnapinsToLoad)
    {
        if (get-pssnapin $snapin -ea "silentlycontinue") 
        {
            write-host "$snapin is already loaded."
        }
        elseif (get-pssnapin $snapin -registered -ea "silentlycontinue") 
        {
            Add-PSSnapin $snapin
            Write-Host "$snapin is now loaded."
        }
        else 
        {
            write-host "PSSnapin $snapin not found" -foregroundcolor Red
        }
    }
}
 
#############################
# START SCRIPT
#############################
 
$started = Get-Date
 
#Load Requested Snapins
LoadSnapins
 
# Initialize Connection for Storage Center
#$pass = Read-Host -AsSecureString -Prompt "Please provide the Storage Center password for $user"
$securepass = ConvertTo-SecureString $pass -AsPlainText -Force
$connection = Get-SCConnection -HostName $schost -User $user -Password $securepass -Save $schost -Default
 
# Create new Volume Folder if it doesn't exist
$volumefolder = Get-SCVolumeFolder -Name $SCParentFolderName
if($volumefolder -eq $null)
{
    Write-Output "Creating new volume folder: $SCParentFolderName..."
    $volumefolder = New-SCVolumeFolder -Name $SCParentFolderName;
}
 
# Get server information for the server that we are mapping all of the volumes to
$scserver = Get-SCServer -Name $ServerName
 
# Create New Mount Point Volumes for database and logs (if requested)
if($createMountpointRoot -eq $true)
{
    CreateVolume "$ServerName-Exchange-MP" "1G" $dbRootDrive $dbDiskFolder $false
}
 
# Reset counters
$dbcount = 1
 
# Loop through total amount of databases for the server
do
{
    CreateVolume "$ServerName-DB$dbcount" $dbsize $dbmproot $dbDiskFolder $true
    
    "Creating mailbox database in Exchange 2010..."
    New-MailboxDatabase -Server $ServerName -Name "$ServerName-DB$dbcount" -EdbFilePath "$dbmproot\$ServerName-DB$dbcount\$ServerName-DB$dbcount.edb" -LogFolderPath "$dbmproot\$ServerName-DB$dbcount\Logs\"
    
    "Mounting new mailbox database..."
    Mount-Database -Identity "$ServerName-DB$dbcount"
    
    $dbcount ++ 
}
until($dbcount -eq $dbtotal + 1)
 
 
# Complete!
$ended = Get-Date
Write-Output "Volume Creation Complete!"
Write-Output "Started: $started"
Write-Output "Finished: $ended"
 
#############################
# END SCRIPT
#############################

There are a number of areas in which this script can be improved and that I will continue to work on. 

Exception handling is very important.  Understanding how your code could react in particular scenarios is difficult, but you don’t want your script to bomb out every time you run it either.  I’ve build quite a bit of exception handling into the mapping and mounting portions of the script, but this can always be reworked to be improved.

Thursday, November 19, 2009 12:53 AM Central Time
Posted by Justin

If you are one of the lucky few to be attending PDC in Los Angeles, CA this week, you probably got a sneak peak at Internet Explorer 9.  On Wednesday, the IE team showed off IE9 performance and standards compliance. 

They provide some interesting data points (and charts) on performance across browser platforms.  Interestingly enough, they show that their score on the Acid3 Test, a test widely known to test for compliance in, was a meek 32 out of 100.  We’ll assume that is when tested against IE8.

You can read more from the IE team on this topic here.

Sunday, September 13, 2009 9:22 PM Central Time
Posted by Justin

I’m working on a project right now and have noticed that XAML seems to be incorrectly rendered in the Visual Studio designer, versus it properly being shown in the Expression Blend 3. 

It seems that sizes and alignments are completely off in Visual Studio 2008 where everything appears correct, renders properly, and executes normally when created in Expression Blend 3.

Here’s an example of XAML as rendered in Visual Studio 2008:

image

and in Expression Blend 3:

image

Ignore the black background on the Expression screenshot, but notice that the buttons and all aspects are alligned properly, where the screenshot from Visual Studio 2008 is not.

Here is the XAML source:

<Window x:Class="QuickTwit.AccountWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="Accounts" mc:Ignorable="d" Height="300" Width="300" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
    <Grid>
        <ListBox Margin="12,12,0,124" Name="listAccounts" HorizontalAlignment="Left" Width="188" SelectionChanged="listAccounts_SelectionChanged" />
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,7" Name="buttonOK" VerticalAlignment="Bottom" Width="75" Click="buttonOK_Click">OK</Button>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,20,12,0" Name="buttonDeleteAccount" VerticalAlignment="Top" Width="75" Click="buttonDeleteAccount_Click" IsEnabled="False">Delete</Button>
        <Rectangle Stroke="#FFC9C9C9" Margin="13,0,12,41" VerticalAlignment="Bottom" Height="75" RadiusX="10" RadiusY="10" Opacity="0.5">
            <Rectangle.Effect>
                <DropShadowEffect BlurRadius="1" ShadowDepth="1" Opacity="0.5"/>
            </Rectangle.Effect>
        </Rectangle>
        <TextBox Height="23" Margin="0,0,27,79" Name="textUsername" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="160" />
        <Label Height="28" HorizontalAlignment="Left" Margin="28,168,0,0" Name="label1" VerticalAlignment="Top" Width="69">Username:</Label>
        <Button x:Name="buttonAuthorize" VerticalAlignment="Bottom" Content="Authorize" Margin="0,0,27,49.04" HorizontalAlignment="Right" Width="75" Click="buttonAuthorize_Click" />
    </Grid>
</Window>

If you have seen this happen or know how to fix it, let me know.