Get Linux disk space report in PowerShell

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

I use my own SSH from PowerShell module to execute the command "df --portability" on the remote Linux hosts to retrieve disk usage data, and then parse the results using a couple of regular expressions, to produce standardized and sortable custom PowerShell objects with numerical and string types.

The code is provided under the MIT license.

NB! This code shows the behavior of the "old" module. In the new version 2 fork you will do it slightly differently (index into the "Result" property to get the text to parse). I currently don't have the energy to update, plus this is useful for people using the old module, so for now I'm just adding this information here at the top.

I'll try to demonstrate how you can use it in a fairly acceptable way.

The code in this article is written to be compatible with use from PowerShell version 2 and up. PowerShell v2 comes built in with Windows 7 and Windows Server 2008 R2 (and is available for XP/2003 R2).

This screenshot sums it up pretty well.

Parse-Linux-df-output-from-PowerShell.png



A single target computer

Connect to the remote Linux hosts using New-SshSession from the SSH-Sessions module there's a link to above.

Here I connect to the computer ubuntuvm. I already had a session to the IP you see in the output from Get-SshSession.

PS C:\Dropbox\PowerShell> New-SshSession -Comp ubuntuvm -User joakimbs # prompted for password in ISE
Successfully connected to ubuntuvm

PS C:\Dropbox\PowerShell> Get-SshSession | Format-Table -AutoSize

ComputerName Connected
------------ ---------
192.168.1.77      True
ubuntuvm          True

Then I just get the output from the Linux command "df --portability". The --portability parameter makes it a bit easier to parse programmatically. Only tested against Ubuntu 14.04 and Centos6, but this should be fairly consistent across GNU+Linux - I think.

PS C:\PS> $dfoutput = Invoke-SshCommand -Comp ubuntuvm -Command 'df --portability' -Quiet

PS C:\PS> $dfoutput[0] 
Filesystem     1024-blocks    Used Available Capacity Mounted on
udev               1014160       4   1014156       1% /dev
tmpfs               204992    1332    203660       1% /run
/dev/sda1         23606716 7834904  14549620      36% /
none                     4       0         4       0% /sys/fs/cgroup
none                  5120       0      5120       0% /run/lock
none               1024960     144   1024816       1% /run/shm
none                102400      32    102368       1% /run/user

That's fine for manual, visual inspection, but if we would like to generate reports or something to trigger on a condition like "above 90 % use should generate a warning", or whatever, we will need to parse the results into objects with the disk sizes and percentages turned into numerical properties.

Understanding the data structure we get from Invoke-SshCommand can be a bit less than intuitive, but it's really an array of single strings that contain newlines. If you write the $dfoutput to file with something like:

$dfoutput[0] | Set-Content Linux-disk-usage.txt

It will be turned into several lines in the file, so if you're parsing data from files, you will need to join them into one long, newline-separated string, for instance like this:

$MyDfOutput = (Get-Content Linux-disk-usage.txt) -join "`n"

With PowerShell version 3 and up you can use the -Raw parameter for Get-Content, like this:

$DfOutput = Get-Content -Raw -LiteralPath Linux-disk-usage.txt

Parsing the Linux utility df's output

So here comes the magic part that turns the df output into custom PowerShell objects. I wrote a ConvertFrom-LinuxDfOutput function that takes a multiline, single string as input (as produced from SSH-Sessions).

Here's the code to store in a file called "ConvertFrom-LinuxDfOutput.ps1":

#requires -version 2
function ConvertFrom-LinuxDfOutput {
    param([string] $Text)
    [regex] $HeaderRegex = '\s*File\s*system\s+1024-blocks\s+Used\s+Available\s+Capacity\s+Mounted\s*on\s*'
    [regex] $LineRegex = '^\s*(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\s*%)\s+(.+)\s*$'
    $Lines = @($Text -split '[\r\n]+')
    if ($Lines[0] -match $HeaderRegex) {
        foreach ($Line in ($Lines | Select -Skip 1)) {
            [regex]::Matches($Line, $LineRegex) | foreach {
                New-Object -TypeName PSObject -Property @{
                    Filesystem = $_.Groups[1].Value
                    '1024-blocks' = [decimal] $_.Groups[2].Value
                    Used = [decimal] $_.Groups[3].Value
                    Available = [decimal] $_.Groups[4].Value
                    CapacityPercent = [decimal] ($_.Groups[5].Value -replace '\D')
                    MountedOn = $_.Groups[6].Value
                } | Select Filesystem, 1024-blocks, Used, Available, CapacityPercent, MountedOn
            }
        }
    }
    else {
        Write-Warning -Message "Error in output. Failed to recognize headers from 'df --portability' output."
    }
} 

You can download it here: ConvertFrom-LinuxDfOutput.ps1.txt. Right-click, save as, rename to .ps1, unblock and dot-source.

And it works like this:

PS C:\PS> . .\ConvertFrom-LinuxDfOutput.ps1 # dot-sourcing
PS C:\PS> ConvertFrom-LinuxDfOutput -Text $dfoutput[0] | ft -a

Filesystem 1024-blocks    Used Available CapacityPercent MountedOn     
---------- -----------    ---- --------- --------------- ---------     
udev           1014160       4   1014156               1 /dev          
tmpfs           204992    1332    203660               1 /run          
/dev/sda1     23606716 7834904  14549620              36 /             
none                 4       0         4               0 /sys/fs/cgroup
none              5120       0      5120               0 /run/lock     
none           1024960     144   1024816               1 /run/shm      
none            102400      32    102368               1 /run/user     

Multiple target computers

And now back to the beginning where we processed all the computers we have a connection to, as provided from Get-SshSession.

This can be done as follows.

PS C:\Dropbox\PowerShell> Get-SshSession | Select -Exp ComputerName | 
    %{ $c = $_; ConvertFrom-LinuxDfOutput -Text (Invoke-SshCommand -Comp $_ -Command 'df --portability' -q) } |
    Select @{n='ComputerName';e={$c}}, * | ft -AutoSize

ComputerName Filesystem                   1024-blocks    Used Available CapacityPercent MountedOn     
------------ ----------                   -----------    ---- --------- --------------- ---------     
192.168.1.77 /dev/mapper/VolGroup-lv_root    51475068 3675556  45178072               8 /             
192.168.1.77 tmpfs                             961124       0    961124               0 /dev/shm      
192.168.1.77 /dev/sda1                         487652  153394    308658              34 /boot         
192.168.1.77 /dev/mapper/VolGroup-lv_home    70608288   53072  66961796               1 /home         
ubuntuvm     udev                             1014160       4   1014156               1 /dev          
ubuntuvm     tmpfs                             204992    1332    203660               1 /run          
ubuntuvm     /dev/sda1                       23606716 7834904  14549620              36 /             
ubuntuvm     none                                   4       0         4               0 /sys/fs/cgroup
ubuntuvm     none                                5120       0      5120               0 /run/lock     
ubuntuvm     none                             1024960     144   1024816               1 /run/shm      
ubuntuvm     none                              102400      32    102368               1 /run/user     

Testing has been limited (in fact to what you see here)! Your mileage may vary.

Random example

PS C:\PS> $temp = Get-SshSession | Select -Exp ComputerName | 
    %{ $c = $_; ConvertFrom-LinuxDfOutput -Text (Invoke-SshCommand -Comp $_ -Command 'df --portability' -q) } |
    Select @{n='ComputerName';e={$c}}, *

PS C:\PS> $temp.Count
11

PS C:\PS> $temp | ft -A 

ComputerName Filesystem                   1024-blocks    Used Available CapacityPercent MountedOn     
------------ ----------                   -----------    ---- --------- --------------- ---------     
192.168.1.77 /dev/mapper/VolGroup-lv_root    51475068 3675588  45178040               8 /             
192.168.1.77 tmpfs                             961124       0    961124               0 /dev/shm      
192.168.1.77 /dev/sda1                         487652  153394    308658              34 /boot         
192.168.1.77 /dev/mapper/VolGroup-lv_home    70608288   53072  66961796               1 /home         
ubuntuvm     udev                             1014160       4   1014156               1 /dev          
ubuntuvm     tmpfs                             204992    1332    203660               1 /run          
ubuntuvm     /dev/sda1                       23606716 7834916  14549608              36 /             
ubuntuvm     none                                   4       0         4               0 /sys/fs/cgroup
ubuntuvm     none                                5120       0      5120               0 /run/lock     
ubuntuvm     none                             1024960     144   1024816               1 /run/shm      
ubuntuvm     none                              102400      32    102368               1 /run/user     



PS C:\PS> $temp | Where { $_.Filesystem -eq 'tmpfs' } |
    sort -Descending available | ft -AutoSize

ComputerName Filesystem 1024-blocks Used Available CapacityPercent MountedOn
------------ ---------- ----------- ---- --------- --------------- ---------
192.168.1.77 tmpfs           961124    0    961124               0 /dev/shm 
ubuntuvm     tmpfs           204992 1332    203660               1 /run