Storage Capacity Report - Statystyki dysków na klastrze Hyper-v w PowerShell cd.

25o EHLO

W poprzednim wpisie pokazane zostalo, jak mozna pobawic sie danymi z dysków Cluster Shared Volume pobierajac dane z klastra. Dzis pokaze co mozna szybko wyciagnac z System Center Virtual Machine Manager, poniewaz w nim przechowywane sa centralnie informacje o wszystkich szczególach i konfiguracji maszyn wirtualnych (bez SCVMM równiez mozna zrobic podobne raporty, odpytujac kolejne hosty).

Czesc druga: Szczególowe statystyki dysków maszyn wirtualnych

Staram sie pokazac w jaki sposób mozna sobie radzic przy minimum wiedzy poczatkowej tak, zeby nie uczyc sie na pamiec. PowerShell jest pod tym wzgledem fantastyczny [obiektowosc tak po prostu ma (: ]. Jednak obiekty VMM sa bardzo zlozone i np. funkcja format-custom nie bedzie zbyt przydatna ze wzgledu na ilosc danych. Wiadomo na pewno, ze dyski podpiete sa do maszyny wirtualnej a to, co trzeba zapamietac, to ze o maszyne wirtualna odpytuje sie przy pomocy get-SCVirtualMachine. Nie podajac zadnej nazwy - zwracane sa wszystkie maszyny. a Wiec jest to swietny poczatek, bo na 'dzien-dobry' mamy wszystkie dane do obróbki pojedynczym poleceniem. Jak nazywa sie parametr zawierajacy informacje o dyskach? z duzym prawdopodobienstwem bedzie zawieral slowo 'disk':

C:\scriptz :))o- $VirtualMachines=get-SCVirtualMachine

C:\scriptz :))o- $VirtualMachines|get-member|? name -like *disk* 

   TypeName: Microsoft.SystemCenter.VirtualMachineManager.VM

Name MemberType Definition
---- ---------- ----------
DiskIO Property int DiskIO {get;}
DiskResources Property System.Collections.Generic.List[Microsoft.VirtualManager.Remoting.ClusterResourceData]...
HasPassthroughDisk Property bool HasPassthroughDisk {get;}
PassThroughDisks Property System.Collections.Generic.List[Microsoft.SystemCenter.VirtualMachineManager.StorageDi...
PerfDiskBytesRead Property long PerfDiskBytesRead {get;}
PerfDiskBytesWrite Property long PerfDiskBytesWrite {get;}
UndoDisksEnabled Property bool UndoDisksEnabled {get;}
VirtualDiskDrives Property Microsoft.SystemCenter.VirtualMachineManager.VirtualDiskDrive[] VirtualDiskDrives {get;}
VirtualHardDisks Property Microsoft.SystemCenter.VirtualMachineManager.StandaloneVirtualHardDisk[] VirtualHardDi...

Widac, ze sa dwie kolekcje - VirtualHardDisks oraz VirtualHardDrives. Dalej sprawdzajac get-member mozna odkryc, ze Hard Disks jest bardziej ogólna i zawiera w sobie kolekcje Hard Drives, a wiec finalnie o dyski mozna zapytac o dwa sposoby, dajace ten sam wynik:

 $vdisks=$vm.VirtualDiskDrives.virtualHardDisk

lub

 $vdisks=$vm.virtualHardDisks

I znów najprostsza weryfikacja bedzie teraz po prostu wyswietlenie $vdisks z odpowiednim wyborem pól, ale upieksze troche calosc, wykorzystujac wiedze z poprzedniego wpisu. Tutaj problem jest podobny - wyswietlajac same dyski, gubi sie informacja dotyczaca maszyny, do której sa podpiete. Poniewaz dyski maja czesto nazwy szablonów lub GUIDów, ciezko bedzie dopasowac. W zwiazku z tym tak, jak poprzednio, nalezy utworzyc wlasny obiekt, rozszerzajac istniejacy o brakujace pola.

import-module virtualmachinemanager
$diskReport=@()
$virtualMachines=Get-SCVirtualMachine
foreach($vm in $virtualMachines){
 $vmname=$vm.name
 $vdisks=$vm.virtualHardDisks
 foreach($vd in $vdisks) {

          #obiekt dysku wirtualnego rozszerzony o dodatkowe pole 'diskOwner' - nazwe maszyny wirtualnej z obiektu-rodzica
        add-member -InputObject $vd -MemberType NoteProperty -Name "DiskOwner" -Value $vmname -force

         #dodatkowe pole, zawierajace krótka nazwe wolumenu. 
        $volShort=''
        if($vd.location -match 'Volume\d+') {
            $volShort=$matches[0]

        }
        add-member -InputObject $vd -MemberType NoteProperty -Name "volumeShort" -Value $volShort -force
        $diskReport+=$vd
    }
}

$diskReport

Chwila wyjasnienia dotyczaca krótkiej nazwy wolumenu. Nazwy te [standardowo] zawsze zaczynaja sie na 'c:\ClusterStorage\' i dalej jest 'Volume' z kolejna cyferka. Mozna obciac kawalek poczatkowego ciagu, ale mozna wykorzystac wyrazenie regularne [+1 do fajnosci]. Operacja '-match' wyszukuje wystapien wyrazenia, tutaj zdefiniowanego jako 'Volume\d+' czyli ciag 'Volume' zakonczony jedna lub wieksza iloscia cyfr ('\d' - cyfra, '+' - jedna lub wiecej). Wynik jest przechowywany w tablicy $matches.

Poniewaz $diskReport jest tablica pelnoprawnych obiektów, mozna latwo wyswietlic to, co sie potrzebuje, wyeksportowac do arkusza csv itd.. o np. tak:

$diskReport|select diskowner,`
@{N='CurrentHost';E={$_.vmhost}},`
name,`
@{N='maxSize (GB)';E={[math]::round($_.maximumsize/1GB,2)}},`
@{N='size (GB)';E={[math]::round($_.size/1GB,2)}},`
volumeShort,location,vhdtype,vhdformattype |`

            export-csv -path DiskReport.csv -delimiter ';' -noTypeInfo

Taki wpis bylby jednak za krótki, a ja lubie drazyc temat, wiec kolejne zadanie: mamy nazwy wolumentów... a które to sa dyski CSV? Rozszerze zatem skrypt o zapytanie dot. CSV i wykorzystam krótka nazwe wolumenu aby je ze soba porównac. Najlatwiejsze wyszukiwanie wykonuje sie na slowniku, w PS realizowanym przy pomocy tzw. hashtable. Przewaga hashtable nad tablica jest taka, ze zawiera klucze (nazwy) i przypisane do nich wartosci. Dzieki temu mozna zdefiniowac, ze np. 'ala' -> 'ma kota' i potem latwo odpytac o konkretny wpis 'jaka wartosc ma "ala"?' nie wykonujac przeszukiwania, jakby to mialo miejsce w przypadku wykorzystania zwyklej tablicy.

Zeby zamiescic juz pelny skrypt, przyprawie go jakimis parametrami, minimum obslugi bledów oraz garscia statystyk - tak przygotowany bedzie nie tylko przydatny, ale i ladny:

[show-VMDisksStatistics.ps1]

<#
.SYNOPSIS
  creates report of disks used by virtual machines.
  requires Virtual Machine Manager
.NOTES
author: nExoR 2o14
version: 2o.1o.2o14
#>
param(
  [parameter(mandatory=$false)][string]$exportFileName="DiskReport-$(get-date -Format MM-yyyy).csv",
  [parameter(mandatory=$false)][string]$clusterName,
  [switch]$export=$true,
  [switch]$showTotals=$true,
  [switch]$showOnScreen
)

function showReport {
    $diskReport|select diskowner,`
                   @{N='CurrentHost';E={$_.vmhost}},`
                   name,`
                   @{N='maxSize (GB)';E={[math]::round($_.maximumsize/1GB,2)}},`
                   @{N='size (GB)';E={[math]::round($_.size/1GB,2)}},`
                   volumeShort,location,CSVName,vhdtype,vhdformattype
}
function showStats {
    #nr of all disks
    $NumberOfVMDisks=($diskReport|measure).count
    #- ile zajmuja wszystkie dyski
    $VMDiskSizeTotal=([math]::round(($diskReport|measure -Property size -Sum).sum/1GB,2)).toString()+'GB'
 #- ilosc vhd vs vhdx
    $VMVHDdisks=($diskReport|? VHDFormatType -EQ 'VHD'|measure).count
    $VMVHDXdisks=($diskReport|? VHDFormatType -EQ 'VHDX'|measure).count
 #- ilosc fixed vs dynamic
    $VMDynamicallyExpending=($diskReport|? VHDType -EQ 'DynamicallyExpanding'|measure).count
    $VMFixedSize=($diskReport|? VHDType -EQ 'FixedSize'|measure).count
 #- dyski dyferencyjne
    $VMDifferencing=($diskReport|? VHDType -EQ 'Differencing'|measure).count

    Write-Host "Number of all disks: $NumberOfVMDisks"
    Write-Host "Total size of all disks: $VMDiskSizeTotal"
    Write-Host "Number of VHD vs VHDX: $VMVHDdisks vs $VMVHDXdisks"
    Write-Host "Number of Dynamically Expending vs Fixed: $VMDynamicallyExpending vs $VMFixedSize"
    Write-Host "Number of Differencing Disks: $VMDifferencing"
}

#polaczenie z klastrem i utworzenie slownika CSV
if($clusterName) {
    try{
        $CSVs=Get-ClusterSharedVolume -cluster $clusterName
        $CSVname=@{}
        foreach($csv in $CSVs) {
            $csv.sharedVolumeInfo.friendlyVolumeName -match 'Volume\d+'|Out-Null
            $volShort=$matches[0]
            $CSVname.Add($volShort,$csv.name)
        }
    } catch {
        write-error "Cannot connect to cluster $clusterName"
    }
}

#wlasciwa czesc dotyczaca dysków

import-module virtualmachinemanager
$diskReport=@()
$virtualMachines=Get-SCVirtualMachine
foreach($vm in $virtualMachines){
 $vmname=$vm.name
 $vdisks=$vm.VirtualDiskDrives.virtualHardDisk
 foreach($vd in $vdisks) {
        add-member -InputObject $vd -MemberType NoteProperty -Name "DiskOwner" -Value $vmname -force
        $volShort=''
        $csvn=''
        if($vd.location -match 'Volume\d') {
            $volShort=$matches[0]
            if($CSVname.ContainsKey($volShort)) {
                $csvn=$CSVname.Item($volShort)
            }

        }
        add-member -InputObject $vd -MemberType NoteProperty -Name "volumeShort" -Value $volShort -force
        add-member -InputObject $vd -MemberType NoteProperty -Name "CSVName" -Value $csvn -force
        $diskReport+=$vd
    }
}

if($export) {
 showReport|export-csv -path $exportFileName -delimiter ';' -noTypeInformation
 write-host "Stats exported to $exportFileName."
}
if($showOnScreen) { showReport }
if($showTotals) { showStats }

Jesli dyski CSV nazywane sa zgodnie z ich typem [np. SATA_RAID5, SAS_RAID0], to dodajac informacje o CSV widac w raporcie odpowiedzi na pytania "czy dyski logów na odpowiednich CSV?" lub "czy maszyny business-critical sa na dyskach o najwiekszej wydajnosci/bezpieczenstwie?" .

Na koniec

Jak to bywa ze skryptami - uzalezniaja. Uda sie odpowiedziec na 1o pytan, ale zaraz jest 1o kolejnych - gdzie sa dyski 'parent' dla dysków dyferencyjnych? Jak zrobic porównanie z wynikami z poprzednich miesiecy? ... od razu przypomina mi sie stara piosenka "bo skrypty, bo skrypty, bo skrypty sa od tego, aby bawic sie, bawic sie, bawic sie na calego" (; majac takie narzedzie, reszta jest kwestia waszej fantazji.

Dodam jeszcze, ze z dyskami dyferencyjnymi zabawa na prawde dopiero sie zaczyna, bo jesli porówna sie wartosci przedstawiane przez VMM w stosunku do realnych liczb zajetosci na dysku, to troche ciezko stwierdzic jak one sie do siebie maja. To bardziej skomplikowana matematyka, niz sie tego oczekuje - ale to równiez juz nie dzisiaj.

eN.

Author: nExoR