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

25o EHLO

Raporty to podstawa zarówno w rozmowie z biznesem, planowaniu rozbudowy srodowiska, proaktywnemu dzialaniu czy po prostu dobremu zarzadzaniu. Przy wiekszych srodowiskach dosc duzym problemem staje sie zarzadzanie przestrzenia - wiadomo, takie zabawki troche kosztuja i póki jest duzy zapas nie trzeba sie troszczyc o lokacje maszyn. kiedy jednak z jednej strony srodowisko sie rozrasta, a biznes zaciska pasa i kolejnej pólki dyskowej nie bardzo chce dostawic, zaczyna sie coraz intensywniejsze wykorzystywanie 'Storage Migration' i 'ukladanie' maszyn tak, aby wszedzie zostala odpowiednia ilosc miejsca. Scenariuszy, w których raportowanie bylo niezbedne, spotkalem wiele. Przykladowe:

  • Sa CSV utworzone na róznych typach RAID. Czy lokalizacja maszyn i dysków jest optymalna? glównie chodzi oczywiscie o wszelkiego rodzaju bazy danych.
  • Jakis n00b wymyslil tragiczny sposób tworzenia CSV - jest wiele drobnych dysków powodujacych spora fragmentacje przestrzeni. Jak ulokowac maszyny, zeby zoptymalizowac przestrzen?
  • Trzeba kupic nowa pólke za *dziesiat-tysiecy-€?? A co jest nie tak z obecna? [a na drugi dzien: prosze dla dzialu DEV przygotowac 5 nowych wirtualek do projektu.. (; ]
  • Podczas tworzenia backpów proces zaczyna blokowac CSV i staja uslugi krytyczne. Póki sie tego nie naprawi trzeba tak poustawiac maszyny, zeby oddzielic je pod wzgledem kalendarza backpów i krytycznosci.

Do raportowania, jak zreszta do wiekszosci operacji, najlepszy jest PowerShell (: Jest oczywiscie wiele mozliwosci i narzedzi ale... nie ma to jak przygotowac sobie samemu i móc dynamicznie wykorzystac do nowego scenariusza.

Jest kilka poziomów, z których mozemy wykonywac zapytania - mozna odpytywac sie systemów operacyjnych, klastra, SC VMM, Hyper-v ... i do tego wykorzystywac rózne mechanizmy - czy to natywne commandlety czy dziargac w WMI. Srodowisko, dla którego beda ponizsze opisy, sklada sie z systemów Windows Server 2o12 lub nowszych oraz System Center Virtual Machine Manager 2o12 lub nowszy. Niektóre skrypty trzeba bedzie troche pozmieniac, zeby dzialaly dla w2k8R2.

Czesc pierwsza: Ogólne statystyki CSV

W przypadku CSV najszybsza metoda bedzie odpytanie klastra - w koncu przechowuje on wszystkie te informacje, a skoro widac je w interfejsie, to znaczy, ze da sie je latwo wydobyc. Dosc szybko mozna odnalezc commandlet, który pokazuje informacje o CSV - get-ClusterSharedVolume. Wyniki jednak sa zaskakujaco ubogie:

C:\scriptz:))o- Get-ClusterSharedVolume -Cluster CLUSTERNAME | fl *

Name : CSV_01_SAS_RAID5
State : Online
OwnerNode : HOST01
SharedVolumeInfo : {C:\ClusterStorage\Volume5}
Id : b4034f2-0ae0-4744-a598-12017f692c08

Name : CSV_02_NSAS_RAID10
State : Online
OwnerNode : HOST2
SharedVolumeInfo : {C:\ClusterStorage\Volume4}
Id : 234ee105-4a10-4535-8312-ca634bfaaa5d

[...]

Jednym z najbardziej pomocnych polecen w PS jest get-Member. Kiedy nie wiadomo z jakim obiektem ma sie do czynienia, szybko mozna dowiedziec sie, co sie da z niego wycisnac. Ma jednak jedna podstawowa wade, która wychodzi w przypadkach takich jak ten:

C:\scriptz:))o- Get-ClusterSharedVolume -Cluster CLUSTERNAME | gm

   TypeName: Microsoft.FailoverClusters.PowerShell.ClusterSharedVolume

Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Update Method void Update()
Id Property string Id {get;}
Name Property string Name {get;set;}
OwnerNode Property Microsoft.FailoverClusters.PowerShell.ClusterNode OwnerNode {get;}
SharedVolumeInfo Property System.Collections.Generic.ICollection[Microsoft.FailoverClusters.PowerShell.ClusterShar...
State Property Microsoft.FailoverClusters.PowerShell.ClusterResourceState State {get;}

Co prawda widac, ze sa rózne wlasciwosci, ale nie jest takie oczywiste co z tym dalej zrobic. Na pewno warto zwrócic uwage, ze jedna z nich jest kolekcja - a wiec mozna odpytywac sie dalej o wlasciwosci tejze kolekcji. Tak jednak znajdziemy kolejne parametry bedace kolekcja i zabawa zaczyna byc nudna. A jest prostszy sposób, który wyswietli od razu caly obiekt wraz rozwinieciem kolekcji potomnych. Sluzy do tego format-custom:

C:\scriptz:))o- Get-ClusterSharedVolume -Cluster CLUSTERNAME | fc *

class ClusterSharedVolume
{
  Name = CSV_01_SAS_RAID5
  State = Online
  OwnerNode =
    class ClusterNode
    {
      Name = HOST1
      Id = 1
      State = Up
    }
  SharedVolumeInfo =
    [
      class ClusterSharedVolumeInfo
      {
        FaultState = NoFaults
        FriendlyVolumeName = C:\ClusterStorage\Volume5
        Partition =
          class ClusterDiskPartitionInfo
          {
            Name = \\?\Volume{90d9c83a-656c-4b04-bc1c-16768219a28c}\
            DriveLetter =
            DriveLetterMask = 0
            FileSystem = CSVFS
            FreeSpace = 458293813248
            MountPoints =
              [
              ]

            PartitionNumber = 1
            PercentFree = 44,75537
            Size = 1023997374464
            UsedSpace = 565703561216
            HasDriveLetter = False
            IsCompressed = False
            IsDirty = Unknown
            IsFormatted = True
            IsNtfs = False
            IsPartitionNumberValid = True
            IsPartitionSizeValid = True
          }
        PartitionNumber = 1
        VolumeOffset = 1048576
        MaintenanceMode = False
        RedirectedAccess = False
      }
    ]

  Id = b4034f2-0ae0-4744-a598-12017f692c08
}

[...]

Tutaj widac juz pelne rozwiniecie otrzymywanego obiektu, co mozna z nim zrobic i jak nazywaja sie poszczególne kolekcje. Trzeba zwrócic uwage na "SharedVolumeInfo =" a dalej wewnatrz na "Partition =" poniewaz wewnatrz Partition jest juz smietanka - dane, które nas interesuja. I tak mozna szybko skleic:

Get-ClusterSharedVolume -Cluster atmcluster|%{$_.sharedVolumeInfo.partition}

Od razu jednak rzuca sie w oczy problem - widac wielkosci partycji, ale nie widac na których sa CSV. Aby uzyskac wszystkie informacje, trzeba bedzie niestety odpytac kolejne kolekcje. Moze sie to wydawac zagmatwane, ale proponuje popatrzec na wydruk z przykladu z format-custom. Widac tam hierarchie, która odzwierciedlona jest zapytaniem w glab:

[show-CSVsInfo.ps1]

$CSVs=Get-ClusterSharedVolume -Cluster CLUSTERNAME
foreach($csv in $CSVs) {
    $SVI=$csv.sharedVolumeInfo
    foreach($vol in $SVI) {
        $partitions=$vol.partition
        foreach($part in $partitions) {
            write-host "$($csv.name),$($csv.ownernode.name),$($SVI.FriendlyVolumeName),$(([math]::Round($part.FreeSpace/1GB,2)).toString()+'GB'),$($part.percentFree),$(([math]::Round($part.FreeSpace/1GB,2)).toString()+'GB')"
        }
    }
}

Od razu napisze, ze to bardzo marny skrypt - co prawa widac to, co nas interesuje ale jest sprzeczny z idea PowerShell - nie zwraca obiektu, a co za tym idzie nie da sie tego dalej przeformatowac, dynamicznie cos zmienic czy w np. zrzucic tego ladnie do CSV. trzeba by to wszystko samemu oprogramowac. W tej postaci skrypt nadaje sie do 'wyswietlenia na ekran' ale porzadnego raportu, czyli takiego, który bedzie mozna otworzyc w arkuszu kalkulacyjnym i cos z tymi danymi dalej zrobic - nie za bardzo.

Jednym z najwiekszych misteriów w PowerShell jest dla mnie fakt, ze pomimo, iz w PS wszystko jest obiektowe i wszystko wokól obiektów sie kreci, to w zasadzie nie ma klas (beda w PS5). Tworzenie obiektów i operowanie nimi jest troche kulawe. Ale to jedyny sposób aby osiagnac pozadany wynik:

[show-CSVsInfo.ps1]

$CSVs=Get-ClusterSharedVolume -Cluster CLUSTERNAME
foreach($csv in $CSVs) {
    $volInfo = New-Object PSObject
    Add-Member -InputObject $volInfo -MemberType NoteProperty -Name CSVname -value $csv.name -Force
    Add-Member -InputObject $volInfo -MemberType NoteProperty -Name CSVowner -value $csv.OwnerNode.Name -force

    $SVI=$csv.sharedVolumeInfo
    Add-Member -InputObject $volInfo -MemberType NoteProperty -Name CSVFriendlyName -value $SVI.FriendlyVolumeName -force

    foreach($vol in $SVI) {
        $partitions=$vol.partition
        foreach($part in $partitions) {
            Add-Member -InputObject $volInfo -MemberType NoteProperty -Name CSVFreeSpace -value $([math]::Round($part.FreeSpace/1GB,2)) -force
            Add-Member -InputObject $volInfo -MemberType NoteProperty -Name CSVPercentFree -value $part.percentFree -force
            Add-Member -InputObject $volInfo -MemberType NoteProperty -Name CSVSize -value $([math]::Round($part.size/1GB,2)) -Force
            $volInfo
        }
    }
}

Calosc jest nieco kulawa, poniewaz kazda partycja, dla kazdego SVI dla kazdego CSV to oddzielny obiekt. Stad parametr '-force' który nadpisuje poprzednie wartosci. Ale tak przygotowany skrypt mozna juz spokojnie przekierowac np. na Export-CSV, wykorzystac Select czy cokolwiek innego [po zapisaniu do pliku show-CSVsInfo.ps1]:

C:\scriptz:))o- .\show-CSVsInfo.ps1 | Export-Csv -delimiter ';' -noTypeInformation -path csvstats.csv

EOF

Na koniec jeszcze dwa niuanse:

  • w skrypcie przygotowanym do exportu nie dodaje 'GB' przy rozmiarach. Formatowanie w PS mozna zrobic przy pomocy select a po otwarciu w Excel lepiej miec od razu jako wartosci liczbowe, zeby mozna bylo dokonac obróbki.
  • równiez z powodu Excela delimiter ustawiony jest jako srednik a nie przecinek. To odwieczna tajemnica aka w-files - czemu Excel pliki Comma-Separated-Value tworzy i interpretuje tylko jesli sa oddzielone srednikiem?

W nastepnej czesci zamieszcze opis skryptu do odpytania o szczególy dotyczace wielkosci dysków maszyn wirtualnych.

eN.

author: nExoR