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