Well, what annoyed me in the past was that I had to patch each XenServer patch by patch (no bulk applying) and when used in combination with UCS blades (especially if those have >250GB RAM), it takes ages to keep a pool up-to-date. So I ended up writing yet another script (I know why I hate Citrix XenServer … the XenCenter GUI is lacking sooooo much) which will download new patches from a directory on a HTTP server and then print the lines necessary to apply the patches to all hosts in a pool.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#!/bin/bash

#
# Updates a XenServer to the most current release
#

TMPDIR=$( mktemp -d )

# Figure out the XS release
XS_REL="$( grep PRODUCT_VERSION /etc/xensource-inventory | cut -d\' -f2 )"

#set -x

# Gather the Patchlist/UUID list
UUID="$( xe patch-list | grep ^uuid | awk '{ print $5 }' )"
XSNAME="$( xe patch-list | grep name-label | awk '{ print $4 }' )"

declare -a pUUID=($UUID)
declare -a pXS=($XSNAME)

count=${#pUUID[@]}
count=$((count-1))
for i in $( seq 0 $count ); do
        echo "${pXS[$i]}: ${pUUID[$i]}"
done | sort

# Now get the current patchlist
PATCH_LIST="$( wget -q -O $TMPDIR/digest http://xen.heimdaheim.de/patches/$XS_REL/digest )"

for xs in "${pXS[@]}"; do
        sed -i "/$xs/d" $TMPDIR/digest
done

if [ "$( cat $TMPDIR/digest )" != "" ]; then
	for file in $( cat $TMPDIR/digest ); do
		wget -q -O $TMPDIR/$file http://xen.heimdaheim.de/patches/$XS_REL/$file
	done

	POOL_MASTER="$( xe host-list uuid=$( xe pool-list | grep master | \
			awk '{ print $4 }' ) | grep name-label | awk '{ print $4 }' )"

	while true; do
		read -p "New XenServer updates found. Should we continue ? (y/n) " yn
		case $yn in
			[Yy]* ) break;;
			[Nn]* ) rm -rf $TMPDIR; exit;;
			* ) echo "Please answer yes or no.";;
		esac
	done

	# Upload the patchfiles to the pool master
	for xs in $( grep -v iso $TMPDIR/digest ); do
		echo -n "Uploading XS patch $xs"
		xe patch-upload file-name=$TMPDIR/$xs 1>/dev/null
		echo "..."
	done

	UUID="$( xe patch-list | grep ^uuid | awk '{ print $5 }' )"
	XSNAME="$( xe patch-list | grep name-label | awk '{ print $4 }' )"

	declare -a PUID=($UUID)
	declare -a XS=($XSNAME)

	count=${#PUID[@]}
	count=$((count-1))

	# Write the stuff to a csv, so we can sort it
	for i in $( seq 0 $count ); do
		echo "${XS[$i]};${PUID[$i]}"
	done | sort > $TMPDIR/patchlist.csv

	echo "The following commands need to be issued on the pool master $POOL_MASTER:"
	echo

	for patch in $( cat $TMPDIR/patchlist.csv ); do
		XS="$( echo $patch | cut -d\; -f1 )"
		UUID="$( echo $patch | cut -d\; -f2 )"

		for host in $( xe host-list | grep ^uuid | awk '{ print $5 }' ); do
			echo "xe patch-apply host-uuid=$host uuid=${UUID}"
		done
		echo
	done

	if [ "$( grep iso $TMPDIR/digest )" != "" ] ; then
		echo
		echo "The following commands need to be issued on each host individually:"

		for xs in $( grep iso $TMPDIR/digest ); do
			cp -f $TMPDIR/$xs /root
			echo "$xs needs to be installed manually."
			echo "This can be achieved with the following commands:"
			echo "   mount -o loop /root/$xs /mnt"
			echo "   cd /mnt"
			echo "   ./install.sh"
			echo "   cd"
			echo "   umount /mnt"
		done
	fi
else
	echo "No updates found!"
fi

rm -rf $TMPDIR

There’s just a little caveat (not because of the script - but of XenServer’s design): if you switch the pool master before the updates are applied to all host, you’ll need to manually delete the patches from the pool (xe patch-delete) and rerun the script on the new pool master …