The better is the enemy of the good…
In a former post I have presented a little monitor script for TFTP services on the NetScaler. After discussing this further with Thomas Berger – among many other things a PVS deep diver – I started to dislike that it only captures the first packet of a file received from the TFTP. Thus, back to the whiteboard. After only minutes of googling I decided to give the Net::TFTP module of the CPAN (Comprehensive Perl Archive Network) a try which was so simple to integrate I regret having spent any time on my own wacky TFTP implementation. It is quite simple…

Installation

  1. Get TFTP.pm from http://search.cpan.org/~gbarr/Net-TFTP-0.19/ and put it on the NetScaler in the directory /flash/nsconfig/monitors
  2. Get nstftp2.pl from here and put it in the same directory
  3. Create a user-space monitor uploading the script below. In script arguments, specify the name of the file and the content to be looked for, like file=monitor.txt;content=COOKIE
  4. If the service uses port *, set the monitor’s destination port to 69.
  5. Bind the monitor to the respective service.

PVS specifics and things to consider

In a PVS scenario, the typical use case is to check the availability of the ARDBP32.bin which is the PVS bootstrapper. Thus, the monitor – by default i. e. when called without additional arguments – downloads this file and looks for “Provisioning Services bootstrap” which is typically contained in this file. In other words, the default parameters are ‘file=ARDBP32.bin;content=Provisioning Services bootstrap’.

Please consider that the file is downloaded completely and cached in RAM. ARDBP32.bin is only a couple of k which is fine with respect to monitor ressource consumption. However, the monitor is probably not a good idea in case of multi megabyte or gigabyte images.

Appendix

The blunt ‘trick’ is to make the perl environment find the Net::TFTP which is done with the ‘use lib’ statement. After that it is a simple use of the library. This procedure is actually quite general and will help you with many Perl-only libraries.

#!/usr/bin/perl -w
## This script is used to <span class="code-keyword">do</span> ftp monitoring using KAS feature.
## The mandatory arguments are:
##   1. File name whose existence will be checked.
## The optional arguments are:
## The arguments must be of the following form:
##      file=&lt;filename&gt;[;content=&lt;string&gt;]
## Example:
##  set monitor ...  -scriptArgs <span class="code-quote">"file=file"</span>

# This sample code is provided to you <span class="code-quote">"AS IS"</span> with no representations,
# warranties or conditions of any kind. You may use, modify and
# distribute it at your own risk.  CITRIX DISCLAIMS ALL WARRANTIES
# WHATSOEVER, EXPRESS, IMPLIED, WRITTEN, ORAL OR STATUTORY, INCLUDING
# WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT.  Without limiting the
# generality of the foregoing, you acknowledge and agree that (a) the
# sample code may exhibit errors, design flaws or other problems,
# possibly resulting in loss of data or damage to property; (b) it may
# not be possible to make the sample code fully functional; and (c)
# Citrix may, without notice or liability to you, cease to make
# available the current version and/or any <span class="code-keyword">future</span> versions of the
# sample code.  In no event should the code be used to support of
# ultra-hazardous activities, including but not limited to life
# support or blasting activities.  NEITHER CITRIX NOR ITS AFFILIATES
# OR AGENTS WILL BE LIABLE, UNDER BREACH OF CONTRACT OR ANY OTHER
# THEORY OF LIABILITY, FOR ANY DAMAGES WHATSOEVER ARISING FROM USE OF
# THE SAMPLE CODE, INCLUDING WITHOUT LIMITATION DIRECT, SPECIAL,
# INCIDENTAL, PUNITIVE, CONSEQUENTIAL OR OTHER DAMAGES, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  Although the copyright
# in the code belongs to Citrix, any distribution of the code should
# include only your own standard copyright attribution, and not that
# of Citrix.  You agree to indemnify and defend Citrix against any and
# all claims arising from your use, modification or distribution of
# the code.
#

use strict;
use Socket;
use Netscaler::KAS;
use lib <span class="code-quote">"/flash/nsconfig/monitors/"</span>;
use Net::TFTP;

## This function is a handler <span class="code-keyword">for</span> performing tftp probe in KAS mode
sub tftp2_probe
{
  ## There must be at least 3 arguments to <span class="code-keyword">this</span> function.
  ##   1. First argument is the IP that has to be probed.
  ##  2. Second argument is the port to connect to.
  ##  3. Arguments to be used during probing.

  <span class="code-keyword">if</span>(scalar(@_) &lt; 3)
    {
      <span class="code-keyword">return</span> (1,<span class="code-quote">"Insufficient number of arguments"</span>);
    }

  ## Parse the argument given, to get file and the content

  my $FILENAME = ( ( $_[2]=~/file=([Monitoring TFTP with Citrix NetScaler revisited (PVS edition)^;]+)/ )  ? $1 : <span class="code-quote">"ARDBP32.BIN"</span> );
  my $CONTENT =  ( ( $_[2]=~/content=([Monitoring TFTP with Citrix NetScaler revisited (PVS edition)^;]+)/ ) ? $1 : <span class="code-quote">"Provisioning Services bootstrap"</span> );

  print $FILENAME . $CONTENT . <span class="code-quote">"\n"</span>;
  # please ignore <span class="code-keyword">this</span>
  # Variable Declaration and Initialization
  my $HOSTNAME = $_[0];
  my $PORTNO = $_[1];
  my $MAXLEN = 1024;

  my $tftp = Net::TFTP-&gt;<span class="code-keyword">new</span>($HOSTNAME, Port =&gt; $PORTNO, BlockSize =&gt; 1024);

  $tftp-&gt;ascii;

  my $fh = $tftp-&gt;get($FILENAME);

  my $store = "";

  <span class="code-keyword">while</span> (my $line = &lt;$fh&gt;) {
    $store .= $line;
  }
  <span class="code-keyword">return</span> (1, <span class="code-quote">"Probe failed - file not retrieved "</span>) <span class="code-keyword">if</span> ( not $store ) ;
  <span class="code-keyword">return</span> (1, <span class="code-quote">"Probe failed - tftp error"</span>) <span class="code-keyword">if</span> ( $tftp-&gt;error ) ;

  warn $store;

  unless ($store =~ /$CONTENT/ )
    {
      <span class="code-keyword">return</span> (1, <span class="code-quote">"Probe failed - Content not Found"</span>)
    }

  ## Probe succeeded
  <span class="code-keyword">return</span> 0;
}

## Register ftp probe handler, to the KAS module.

probe(\&amp;tftp2_probe);