On finding disk usage for XenServer VMs on NFS

In a similar way to needing to find the state of VMs that were on a XenServer storage volume to "do other stuff" with I came across the need to understand the disk usage of XenServer VMs on an NFS volume. Yes: it is the exciting life I lead.

If you consider the XenServer Command Reference, there isn't a direct command for this action (because Virtual Machines have Virtual Block Devices which in turn reference the Virtual Disk Images .. which are what are stored on the disk) 

What XenCentre will do is show you the *expected* size of the VM i.e. what you said the maximum capacity would be. Lovely. If you're on an LVM volume that is what it is, but if you've an NFS volume then you can have more sensible conversations about capacity vs usage - but you can't readily see that information.  

What I wanted to know was:  

a) the storage use of the NFS volume

b) to understand the disk usage per VM

Without a direct xe command, I had to script a solution.

The script below is a bit rough and ready, not a lot of error checking, and I've only run it on v7 but ultimately, it puts out a format showing you each VM's disk's, its physical size, and its disk use size. 

If you're new to XenServer, or indeed non-Windows and not quite sure about where you stand on "this is a unix system, I know this" - actual file reporting size in XenServer (as with any unix system) has an almost Schrödinger's cat principle: it depends how you view it. 

The team over at My Technical Works have a very good description :-

du , df and ls commands show different results for the same Disk block ,the reason is

  • 'ls' gives data on individual files based on the difference between the end-of-file and the beginning-of-file, whether or not blocks were actually allocated to the file
  • 'du' shows the blocks actually allocated to an individual file 
  • 'df' shows the blocks allocated in the entire file system, including inodes and other meta data

When you look at the "storage use" in XenCenter you're looking at the output of "df".. possibly. XenCenter can get a little behind what is actually going on - so if it looks odd; select the volume, select the Storage tab of the volume and press the Rescan button which forces a check on the storage. 

What the script does is take a volume name as input and then queries all the VDI (i.e. disk) instances on that volume. The script then looks to relate the disks back to virtual machine, or snapshot names. Once it finds out all the info, it presents it in a table. 

On reflection I shouldn't have started with bash. If it was in python (for example) I could've made single xe requests because it'd have been easier to manipulate the output; at the end there's a view of snapshots and perhaps it'd be useful to show where "base copy" vdi instances are attributed to in the table itself. Still, it was an interesting exercise. 

Here is the script,  I look forward to your comments.

=Start of Script================================

#!/bin/bash
mult=1024
bytetogb=`echo $mult ^ 3 | bc`
mbtogb=`echo $mult ^ 1 | bc`
tfile="/tmp/$(basename $0).$$.tmp"

function outtofile {
  echo $1 >> $tfile
}

sruuid=`xe sr-list name-label=$1 params=uuid --minimal `
if [ "$sruuid" ==  "" ] ; then
  echo "uknown SR"
  exit
fi
echo 'Calculating...'

srname=`xe sr-list uuid=$sruuid params=name-label --minimal`
srphyutil=`xe sr-list uuid=$sruuid params=physical-utilisation --minimal`
usedGBdu=(`du -h /run/sr-mount/$sruuid`)

usedGBdf=(`df -h /run/sr-mount/$sruuid`)

echo "Vol="$srname > $tfile
outtofile "UUID="$sruuid 
outtofile "PhysUtil (GB)"=`echo $srphyutil / $bytetogb | bc` 
outtofile "Avail (GB)"=${usedGBdf[10]}
outtofile "Used du  (GB)"=${usedGBdu[0]} 
outtofile "Used df  (GB)"=${usedGBdf[9]} 

outtofile "DiskName:VMName:VDIName:VDI-VS:VDI-P:VHD-VS:VHD-PS:ls-h"
outtofile "------"

#likely iterate
IFS=', ' read -r -a vdiarray <<< "`xe vdi-list sr-uuid=$sruuid --minimal`"
totalvirtsize=0
totalphyutil=0
totalvhdvs=0
totalvhdps=0
for vdiuuid in "${vdiarray[@]}"
do
    vdiname=`xe vdi-list uuid=$vdiuuid params=name-label --minimal`
    vmuuid=`xe vbd-list vdi-uuid=$vdiuuid params=vm-uuid --minimal`
    vmname=`xe vm-list uuid=$vmuuid params=name-label --minimal`
    if [ "$vmname" == "" ] ; then
        vmname="[SnapS]"`xe snapshot-list uuid=$vmuuid params=name-label --minimal`
    fi
    virtsize=`xe vdi-list uuid=$vdiuuid params=virtual-size --minimal`
    phyutil=`xe vdi-list uuid=$vdiuuid params=physical-utilisation --minimal`
    location=`xe vdi-list uuid=$vdiuuid params=location --minimal`
    sruuid=`xe vdi-list uuid=$vdiuuid params=sr-uuid --minimal`

    vhd=(`vhd-util query -vs -n /run/sr-mount/$sruuid/$location.vhd`)
    lsh=(`ls -lh  /run/sr-mount/$sruuid/$location.vhd`)

    outtofile "$vdiuuid:$vmname:$vdiname:`expr $virtsize / $bytetogb`:`expr $phyutil / $bytetogb`:`expr ${vhd[0]} / $mbtogb`:`expr ${vhd[1]} / $bytetogb`:${lsh[4]}"
    totalvirtsize=`expr $totalvirtsize + $virtsize`
    totalphyutil=`expr $totalphyutil + $phyutil`
    totalvhdvs=`expr $totalvhdvs + ${vhd[0]}`
    totalvhdps=`expr $totalvhdps + ${vhd[1]}`
done
    outtofile "TOTAL:------:----->:`expr $totalvirtsize / $bytetogb`:`expr $totalphyutil / $bytetogb`:`expr $totalvhdvs / $mbtogb`:`expr $totalvhdps / $bytetogb`" 
column -t -x -s ":" $tfile
rm $tfile
echo
echo "Show snapshot chain"
vhd-util scan -f -c -p  -m /var/run/sr-mount/$sruuid/*.vhd

=End of Script================================

Print