Search code examples
bashlinux-disk-free

producing df command a grand total specific filesystem


Just need to do some basic thing which I could manage on my own but turned out I have doubt on the result I get...

I need to produce a grand total using df command on specific filesystem on two different servers.

ssh server1 df -mP | egrep "/dev/md10|/dev/md11" | sort ; ssh server1 df -mP --total | grep "total" | egrep -v "/dev/md10|/dev/md11

The result:

/dev/md10                                                   183004016  87303581  95700436      48% /si001c1
/dev/md11                                                   183004016 165986430  17017587      91% /si001c2
total                                                       366565396 253332843 113232554      70%

And then I used same command on the 2nd server to get the grand total. My question, the grand total I get seem not right (I'm not sure) and how to produce a grand total by combining both using same command in anyway using single line command or as a basic script in bash. Thanks for the help.

Update: This is an update after following the guide from @EdmCoff.

printf "Grand total: " ; (ssh server1 df -m /dev/md11 ; ssh server2 df -m /dev/md1*) | awk 'a+=$2;b+=$3;c+=$4;d+=$5/3} END {print a" "b" "c" "d%}'

Result:

Grand total: 549012050 501832399 47179652 92%

Built a script (re-done):

#!/bin/bash
(ssh server1 df -m --no-sync "/dev/md11" ; ssh server2 df -m --no-sync "/dev/md1*") | sed -e /^Filesystem/d | sort > df_udsall
#(cat df_udsall)
printf "\nUDS:\nserver1 & server2\n"
printf "Mounted on       Free space   Disk usage   Use"%%"   Disk state"
while read df_udsall 
do
USAGE=$(echo $df_udsall ; echo $grand_total) | awk '{print $5}' | cut -d"%" -f1
if [[ "$USAGE" -ge " 95" ]]
then
STATUS='CRITICAL'
elif [[ "$USAGE" -ge " 90" ]]
then
STATUS='WARNING'
else
STATUS='OK'
fi
printf "$(echo $df_udsall | awk '{print $6, $4, $3, $5}' > df_udsall_stats)"
column -t df_udsall_stats | perl -ne 'chomp ; printf "\n%-18s %8s %12s %6s %-2s", split / +/' ; printf "`echo -ne $STATUS`"
done < df_udsall
printf "\n\nGrand total " ; cat df_udsall | awk '{b+=$4;c+=$3;d+=$5/3} END {print b" "c" "d "%"}' > grand_total
column -t grand_total | perl -ne 'chomp ; printf "%15s %12s %6s %-2s", split / +/' ; printf "`echo -ne $STATUS`"
rm -f df_udsall df_udsall_stats grand_total

The script above need some fine tuning.

Expected result:

UDS:
server1 & server2
Mounted on         Free space   Disk usage   Use%   Disk state
/sic1                16202762    166801255    92%   WARNING
/sic2                15648157    167355860    92%   WARNING
/sic3                15256569    167747448    92%   WARNING

Grand total:         47107488    501904563    92%   OK

Fixed tripleee script output result:

UDS:
server1 & server2
Mounted on         Free space   Disk usage   Use%   Disk state
/sic001c1          92146461     90857556    50% OK
/sic001c2          16873531    166130486    91% WARNING
/sic001c3          16832710    166171307    91% WARNING
/sic001c4          16362388    166641629    92% WARNING

Grand total:       142215090    589800978     81 OK%

Solution

  • Here is an attempt at refactoring your script into a version where Awk is wholly in control of the calculations and formatting of the output, as suggested in several comments. I have inlined comments which you might want to remove.

    #!/bin/bash
    # use a loop to reduce duplication of ssh options etc
    while read -r server mounts; do
        ssh "$server" df -m --no-sync $mounts </dev/null
    done <<\____SERVER__MOUNTS |
        server1 /dev/md10 /dev/md11
        server2 /dev/md12 /dev/md13
    ____SERVER__MOUNTS
    # sed -e /^Filesystem/d inlined into Awk below
    sort |
    # pipe into awk
    awk 'BEGIN { print "UDS:\nserver1 & server2"
            # Maybe use printf here too
            print "Mounted on         Free space   Disk usage   Use%   Disk state"
            fmt="%-18s %8i %12i %6s %-2s"
        }
        function status(num) {
            # Awk conveniently ignores any trailing string
            # so you can compare 95% to 95 numerically
            if (num > 95) result="CRITICAL"
            else if (num > 90) result="WARNING"
            else result="OK"
            return(result)
        }
        ! /^Filesystem/ {
            printf fmt "\n", $6, $4, $3, $5, status($5)
    
            # EdmCoff''s hack, too lazy to change to legible variable names
            # adapted to defer division until we know the final value of n
            a+=$2;b+=$3;c+=$4;dd+=$5;n++
        }
        END { d=dd/n;
            printf "\n" fmt "%%\n",
         "Grand total:", c, b, int(d), status(d) }'
    

    I obviously have no way to test this with real input, so there are probably minor formatting errors etc. I hope I captured enough of the logic to at least show you how to implement the body of this processing as an Awk script.

    Notice the absence of any temporary files (which your original script would fail to clean up if it was interrupted; use trap) and the use of indentation to help you see the loops and conditionals.