Here's a clever way to leverage the NS database to retrieve a list of possible DS image repositories. The included script also includes a ping check to make sure you use the file share with lowest average ping, like Altiris Software Delivery.
The eXpress share
First off create a software package, "eXpress", for the eXpress share (I guess using the dslibrary package works fine also). This package needs no program, it should only contain the OS image files and required drivers.
The software cache folder of this "eXpress" package needs to be shared as "eXpress" on all package servers.
Retrieve the assigned package servers
A software delivery agent gets it's package servers from the "http://NS/altiris/NS/Agent/GetPackageInfo.aspx" url. It takes an XML string as input, and by accessing this URL with a string containg your IP address and the package you are looking for, a XML response is recieved. (KB article here.)
Now, how to access this URL from the linux automation?
The simplest way seems to be using "wget". This application is not included in the linux automation, but you can get it up and running by copying the wget binary and required libs to the DS express share, and reload the lib cache.
The CentOS 4.5 "wget" require the following libs, but you can get them and the wget binary from almost any Linux distribution:
- libc.so.6
- libcrypto.so.4
- libdl.so.2
- libgssapi_krb5.so.2
- libk5crypto.so.3
- libkrb5.so.3
- libpthread.so.0
- libresolv.so.2
- librt.so.1
- libssl.so.4
- libz.so.1
Put them in a directory together with the wget binary on your DS (e.g Script\wget).
In the same wget directory on the DS create a file "url.txt". This will hold the XML string that is going to be sent to GetPackageInfo.aspx.
Since this string is a XML string that is going to be sent over http it does not look pretty (but it really doesn't matter):
http://sespmm45/altiris/NS/Agent/GetPackageInfo.aspx?xml=%3Crequest%20resource=%22%7B549dcf61-6da0-4077-906d-f3fab8c49f62%7D%22%20version=%221%22%20type=%22codebases%22%20compress=%220%22%20totalTime=%220%22%3E%0A%3Cpackages%3E%0A%09%3Cpackage%20guid=%22%7Ba521f923-5044-4bd6-92c6-d653ab0373d4%7D%22%2F%3E%0A%3C%2Fpackages%3E%0A%3Caddresses%3E%0A%09%3Caddress%20ip=%22[ipaddress]%22%2F%3E%0A%3C%2Faddresses%3E%0A%3C%2Frequest%3E%0A
The first variable highlighted should be a computer resource guid and we set it to the NS guid (as we can be certain this guid will always exist).
What's interesting to us is the package guid variable, and this is set to the guid of the "eXpress" software package, containing the OS image(s) and needed drivers. This can be any package guid but it it nice to know that the "eXpress" package is present and in sync on the package server.
The other variable is replaced with a "place holder" [ipaddress], and this has to be replaced with the current ip of the client. Using bash this is no problem!
The script
Here is an example script to mount an "eXpress" share from a local package server. Save it as \\ds\eXpress\Script\MountLocalShare.sh.
#!/bin/bash
#MountLocalShare.sh
export PATH=$PATH:/opt/altiris/deployment/adlagent:/mnt/ds/rdeploy/Linux
#Get ip address of eth0:
IPADDR=`ifconfig eth0 | grep Mask | awk '{split($2,ipaddr,":");printf ipaddr[2] }'`
echo My ip-address is $IPADDR >> /dev/tty1
logevent -c:0 -l:0 -ss:"Connecting with $IPADDR"
#wget path
WGET_PATH=/mnt/ds/Script/wget
if [ -d $WGET_PATH ]; then
#Add the wget lib path to ls.so.conf and recreate the cache of shared libs
echo $WGET_PATH >> /etc/ld.so.conf
echo Running ldconfig >> /dev/tty1
ldconfig
else
echo Path to wget files not found! Aborting... >> /dev/tty1
logevent -c:110 -l:3 -ss:"Path to wget files not found!"
exit 110
fi;
echo Retrieving a list of possible package servers... >> /dev/tty1
if [ -e $WGET_PATH/url.txt ]; then
urlstring=$(cat $WGET_PATH/url.txt)
#Find the string "[ipaddress]" and replace it with IPADDR
echo "${urlstring/\[ipaddress\]/$IPADDR}" > /url.txt
$WGET_PATH/wget -i /url.txt -O /reply.txt
#grep for line containing "<codebase url" and using "/" as field separator print the third column. Pipe to cut and remove possible ":" and port number (if it's an http url). Also we don't want to fetch images from ns
PS=($(grep "<codebase url" /reply.txt | grep -E -i -v "ns" | awk -F/ '{print $3}' | cut -d: -f1))
if [ -n "$PS" ]; then
echo "Returned package server(s): ${PS[@]}" >> /dev/tty1
logevent -c:0 -l:0 -ss:"Returned package server(s): ${PS[@]}"
else
echo "No package servers returned!" >> /dev/tty1
logevent -c:111 -l:3 -ss:"No package servers returned!"
exit 111
fi;
else
logevent -c:112 -l:3 -ss:"Can't find file url.txt"
exit 112
fi;
if [ "${#PS[@]}" -eq 1 ]; then #Only one package server returned
pkgsrv=$PS
echo "Testing: $pkgsrv" >> /dev/tty1
logevent -c:0 -l:0 -ss:"Testing: $pkgsrv"
if ping -I $IPADDR -c3 $pkgsrv 2>/dev/null; then
echo " $pkgsrv: Ping check ok" >> /dev/tty1
logevent -c:0 -l:0 -ss:"$pkgsrv: Ping check ok"
SELECTED_PS=$pkgsrv
else
echo " No reply from $pkgsrv!" >> /dev/tty1
logevent -c:0 -l:0 -ss:"No reply from $pkgsrv!"
fi;
elif [ "${#PS[@]}" -gt 1 ]; then #More than one package server in the array
LOW_AVG=99999
echo Trying to determine the closest package server... >> /dev/tty1
for pkgsrv in "${PS[@]}"; do
echo "Testing: $pkgsrv" >> /dev/tty1
if ping -I $IPADDR -c3 $pkgsrv 2>/dev/null; then
echo " $pkgsrv: Ping check ok" >> /dev/tty1
logevent -c:0 -l:0 -ss:"$pkgsrv: Ping check ok"
#Get the average response time from five pings:
avgping=$(ping -q -c5 $pkgsrv | grep = | awk '{print $4}' | cut -d / -f 2)
echo " $pkgsrv: Average response time: $avgping ms" >> /dev/tty1
if [ $(echo "$avgping < $LOW_AVG" | bc) -eq 1 ]; then #This is the lowest avg so far...
echo " $pkgsrv: New low avg: $avgping ms" >> /dev/tty1
LOW_AVG=$avgping
SELECTED_PS=$pkgsrv
fi;
else
echo " No reply from $pkgsrv!" >> /dev/tty1
logevent -c:0 -l:0 -ss:"No reply from $pkgsrv!"
fi;
done
fi;
if [ -z "$SELECTED_PS" ]; then #SELECTED_PS variable is not initialized... No PS has responded to the ping request
logevent -c:42 -l:3 -ss:"None of the returned package servers are online!"
exit 42
else
echo Selected package server: $SELECTED_PS >> /dev/tty1
logevent -c:0 -l:0 -ss:"Selected package server: $SELECTED_PS"
cd /mnt
mkdir ps
RETVAL=$?
case $RETVAL in
1) logevent -c:1 -l:0 -ss:"mkdir: /mnt/ps already exists."
umount /mnt/ps
;;
0) logevent -c:0 -l:0 -ss:"mkdir: /mnt/ps created."
;;
*) logevent -c:$RETVAL -l:3 -ss:"mkdir: Error creating mount point /mnt/ps"
exit $RETVAL
;;
esac
logevent -c:0 -l:0 -ss:"Connecting with $IPADDR; Using $SELECTED_PS for image deployment."
echo Using $SELECTED_PS for image deployment. >> /dev/tty1
asmbmount -d DOMAIN -f /DSUSER.pwl //$SELECTED_PS/eXpress /mnt/ps
RETVAL=$?
if [ $RETVAL -ne 0 ]; then
logevent -c:$RETVAL -l:3 -ss:"Error mounting $SELECTED_PS/eXpress"
exit $RETVAL
fi;
fi;
#EOF
You will need to replace "ns" in the following line with you Notification server name (Leave the qoutes!)
PS=($(grep "<codebase url" /reply.txt | grep -E -i -v "ns" | awk -F/ '{print $3}' | cut -d: -f1))
And you will also need to replace DOMAIN and DSUSER in the following line of the script:
asmbmount -d DOMAIN -f /DSUSER.pwl //$SELECTED_PS/eXpress /mnt/ps
Now, to fetch a disk image from your closest package server all you need to do is to create a job containing this script and a disk image task.
The DS Job
Run the "\\ds\eXpress\Script\MountLocalShare.sh" script from file
followed by a "Distribute Disk Image..."
You also need to check the "Image is stored locally on the client".
I don't know how popular Linux is as an Altiris automation environment, but we have used it to deploy Windows XP since it first became available in DS.
This script could probably be ported to vbscript and used with the WinPE automation also!