CodeShare
Have some code. Share some code.
cancel
Showing results for 
Search instead for 
Did you mean: 
philh_127905
Nimbostratus
Nimbostratus

Problem this snippet solves:

Version 9 (tested on v9.2.4)

Based on F5's example monitor, with the NTP check added. We make sure that the node gives us an NTP response and that the node hasn't become stratum 16 (ie, it's lost it's source). We use the CPAN Net::NTP module to make things easy.

Just use it like a normal external monitor, it only uses the default IP and port.

Code :

#!/usr/bin/perl -w
###############################################################################
#                                                                             #
# F5 Networks and BIG/ip(c) Copyright                                         #
#                                                                             #
# No part of the software may be reproduced or transmitted in any form or by  #
# any means, electronic or mechanical, for any purpose, without express       #
# written permission of F5 Networks, Inc. It is against the law to copy the   #
# software. No part of this program may be reproduced or transmitted in any   #
# form or by any means, electronic or mechanical, including photocopying,     #
# recording, or information storage and retrieval systems, for any purpose    #
# other than the purchasers personal use, without the express written         #
# permission of F5 Networks, Inc. Copyright (c) 1996-2005 BIG/ip Software. All     #
# rights reserved. Our services are only available for legal users of the     #
# program for instance in case we extend our services by offering updating of #
# files through Internet.                                                     #
#                                                                             #
###############################################################################
#
#
# As for any external monitor program, the first two command
# line arguments are the node ip address and the node port.
# After that its whatever is supplied via the monitor template.
#
# Caveat: This does assume that you're using IPv4.
#

use lib "/shared/lib/perl5";
use strict;
use Net::NTP;
require 5.005;

# Derive and untaint programname.
my $programname = '/' . $0;
$programname =~ m/^.*\/([^\/]+)$/;
$programname = $1;

if ($programname eq '') {
    die "Bad data in program name\n"
}

# Process ID and file where it's to be stored.  The format
# is significant.

my $node = $ARGV[0];
$node =~ s/^::ffff://;
my $port = $ARGV[1];

my $pidfile = "/var/run/$programname.$node..$port.pid";
my $pid = "$$";

# Maintenence.  Clean up any existing EAV.

if (-f $pidfile ) {
    open(PID, "<$pidfile"); 
    my $pid = ; 
    close(PID);
    if ( $pid ) {
        chomp $pid; $pid =~ m/^(\d+)$/; $pid = $1;
        if ( $pid ) {
            kill 9, $pid;
        }
    }
    unlink($pidfile);
}

# Create a new maintenence file.

open(PID, ">$pidfile");
print PID $pid, "\n";
close(PID);

########################################3
##

if (try_ntp_test($node,$port)) {
        print "OK\n";
}

unlink($pidfile);
exit(0);

########


sub try_ntp_test {
        # we just make sure we get a response
        # and that the server isn't a stratum
        # 16 server (ie, it's lost it's time source).

        my ($node,$port) = @_;
        my %response;

        eval {
                %response = get_ntp_response($node,$port);
        };
        if ($@) {
                return 0;
        } else {
                if ($response{'Stratum'} =~ /\d+/) {
                        # if the stratum is a number
                        if ($response{'Stratum'} == 16) {
                                return 0;
                        } else {
                                # and it isn't 16
                                return 1;
                        }
                } else {
                        return 0;
                }
        }

}

Tested this on version:

11.0
Comments
Rich_R
Nimbostratus
Nimbostratus
Where is the code?
JCohen
F5 Employee
F5 Employee
Rao_88575
Nimbostratus
Nimbostratus
For v11.x my $node = $ENV{"NODE_IP"}; my $port = $ENV{"NODE_PORT"}; and add: my $node = $ARGV[0]; my $node = substr $node,7;; my $port = $ARGV[1]; $node =~ s/::ffff//; --> Remove this
Godfrey_Bennett
Nimbostratus
Nimbostratus

Rao, I don't quite understand your suggestion for modification - would it be possible to modify the source file to show exactly what needs to be done? I am interested in testing this on v12.x, which I think should be very close to v11. Thanks

 

Mark_57945
Nimbostratus
Nimbostratus

I updated the code block on this page to include the monitor here, rather than needing to follow the link to the old location.

 

I included Rao's changes to make the monitor compatible with v11 and later. Rather than "$node = substr $node,7;;", I did "$node =~ s/^::ffff://;" to hopefully not break v6 node addresses. (I don't have any v6 to test, but Rao's use use of substr was sure to break them)

 

I also moved Net::NTP from /usr/bin/monitors/CPAN to /shared/lib/perl5 because I don't want the monitor to break when I upgrade Big IP.

 

WeaverJK
Nimbostratus
Nimbostratus

Thanks to philh, Mark S., Rao, and Mark for their contributions.

 

Looking for assistance determining where my use of the above code may have gone wrong.

 

Steps taken:

 

A) Within /shared/lib, created "perl5" directory with default permissions.

 

B) Per this page: https://devcentral.f5.com/s/articles/ntp-monitor-for-11x-with-complete-instructions-999 Downloaded and extracted ntp.zip contents. TFTPed NTP.pm to /shared/lib/perl5 and saved it with 755 permissions.

 

C) Used the code above in a file named "ntp_mon."

 

D) Used the F5 File Management External Monitors section to upload "ntp_mon" as "MON_EXT_NTP."

 

E) Created a Monitor called "MON_NTP" that used "MON_EXT_NTP" as an external monitor.

 

F) Modified the Pool to use the "MON_NTP" monitor.

 

Results: Pool Members showed as red diamond down.

 

I did not use Arguments or Variables when creating the external monitor configuration as neither article mentioned the configuration of arguments or variables.

 

I just re-re-read one of the articles and noticed that it uses only the "default" IP and Port. I wonder what the author means by this. I presumed that the monitor would send an NTP request to the IP address of the Member over its IP and Port. I also assumed that the BIG-IP would be intelligent enough to use a static or floating self IP on the same subnet as Member in order to attempt to perform the NTP query.

 

Thanks for your input,

 

John

 

Mark_57945
Nimbostratus
Nimbostratus

If I read what you did correctly, you created /shared/lib/perl5/NTP.pm. Unfortunately, that isn't how Perl manages it's modules. The way Perl typically stores/accesses it's module files, the :: is translated into a file system directory break.

 

So, for Net::NTP above, stored in /shared/lib/perl5, the full path to NTP.pm that Perl expects would be /shared/lib/perl5/Net/NTP.pm.

 

WeaverJK
Nimbostratus
Nimbostratus

Mark, thanks for your input.

 

Mark, thank you for explaining the Net::NTP section. I Googled it earlier but did not make the connection to the file structure as you explained it. I have no experience with "perl" at this time.

 

I changed the actual directory structure as suggested -> /shared/lib/perl5/Net/NTP.pm. I left the "use lib" statement as per the code in this article (use lib "/shared/lib/perl5";). I used the Configuration Utility to examine the external monitor. I discovered about six instances of invalid characters (was a question mark inside a black diamond). I removed these characters.

 

Tried the monitor again and the members went to red diamond down.

 

Any other thoughts? Thanks!

 

Mark_57945
Nimbostratus
Nimbostratus

'use lib "/shared/lib/perl5";' is correct, so leaving it as-is was the right thing to do.

 

First thing I would do is make sure that your pool config is correct and that your NTP servers really are up and configured correctly. (e.g. responding to remote queries, not stratum 16). I don't know where those invalid characters came from when you downloaded it, everything looks OK when I look at the code on this page. I suspect that you may have broken the code when you deleted the invalid character markers.

 

WeaverJK
Nimbostratus
Nimbostratus

Great point, Mark. I believe I addressed that question in my initial post, which DevCentral trashed as I was not logged in at the time. I had to retype the post and left out the bit about the NTP services functioning.

 

As a temporary measure, when I first configured the monitor, I set up a monitor to check port 123. The Nodes were all Active. The Printers which are using the VS/VIP received their NTP configurations without a problem.

 

The issue is when I remove the "MON_NTP_123" monitor and replace it with the "MON_EXT_NTP" monitor.

 

Other systems using the Nodes directly (not going through the VS/VIP/F5) are also obtaining time synchronization appropriately.

 

I can try deleting and importing the monitor file again, without modifying it, but I truly would expect the program to fail if it contained characters that the F5 was not happy with. Not to say that the code on this page is that way, but with the copying and pasting I had to do on a Mac OS, who knows what characters were thrown into the mix. 😉

 

WeaverJK
Nimbostratus
Nimbostratus

The script above uses arguments instead of environment variables.

 

Are these arguments the IP and Port passed from the Monitor itself? Seems reasonable.

 

Script hasn't worked with or without the invalid characters mentioned above.

 

Thanks for any additional input.

 

Mark_57945
Nimbostratus
Nimbostratus

External monitor input handling changed between version 10 and version 11. In version 10 and earlier, they used environment variables to pass the IP and port. Starting in version 11, the IP and port are passed as argv[0] and argv[1].

 

I don't know what to tell you about the script not working for you. The script above is what I currently have in service.

 

WeaverJK
Nimbostratus
Nimbostratus

Thanks, Mark, for the background info.

 

If you don't mind, let's just walk through the steps again...

 

Should this work: ?

 

SSH or Console to the F5. Navigate to /shared/lib. Create a directory called "perl5" with 755 permissions. (Note: I created the directory and allowed it to inherit permissions). TFTP the NTP.pm file (from the ntp.zip file from the other post) to /shared/lib/perl5/Net/NTP.pm and set the permissions to 755. Copy the text above and paste it into a notepad.exe text file on a Windows computer. Use that Windows computer to connect to the Configuration Utility (CU). Use CU. Go to System > File Management > External Monitor File List (or something close). Import the contents of the text file while providing the external monitor a name.

 

Edit the external monitor to verify that it does not contain invalid characters (such as the question mark inside the diamond). Create a Monitor. Define the type as External. Select the External Monitor. Assign the monitor to a Pool. Pool Members are Nodes that are NTP servers.

 

Did I miss something or should this work?

 

Thanks!

 

John

 

Mark_57945
Nimbostratus
Nimbostratus

That should work.

 

WeaverJK
Nimbostratus
Nimbostratus

Thanks, Mark, for your time and patience. I appreciate the explanations.

 

If/When the monitor works on our systems, I'll follow-up here.

 

WeaverJK
Nimbostratus
Nimbostratus

Is there anything that needs to be done to ensure perl is working? Can the script (external monitor and NTP.pm) be tested from the command line to directly test functionality of the script between the F5 and the NTP server? If so, how?

 

At a quick glance (being new to perl), it looks like I'd have to copy the external monitor file over to the F5 as a perl script (such as TFTPing mon_ntp to /var/tmp/test/mon_ntp.pl), perhaps modify the script so that the arguments (IP and Port) are hard-coded, and call the script (/usr/bin/perl /var/tmp/test/mon_ntp.pl). Sound about right? Is there an easier way to test this and see the results/errors?

 

Thanks in advance!

 

John

 

WeaverJK
Nimbostratus
Nimbostratus

I tried what I just suggested... F5_Prompt perl mon_ntp.pl Unrecognized character \xC2 in column 1 at mon_ntp.pl line 27.

 

I removed the offending characters.

 

F5_Prompt perl mon_ntp.pl syntax error at mon_ntp.pl line 59, near "= ;" Execution of mon_ntp.pl aborted due to compilation errors.

 

Perl appears to have a problem with something around this line:

 

my $pid = ;

 

Going through again to see what may have changed.

 

WeaverJK
Nimbostratus
Nimbostratus

Comparing the code in this thread to the code in the original post (https://devcentral.f5.com/s/articles/ntp-monitor-for-11x-with-complete-instructions-999), I found this difference: Above, line 56: my $pid = ; Original: my $pid = ;

 

When I changed the code back to the original, perl processed further (past line 56).

 

Yeah! This resolved the issue!!!

 

pats self on back

 

I also determined WHY this issue with the code appears above. The code that these F5 web pages is using to parse and display the text treated the "" as something special and this text did not display in the code lines above. I had to preface the less-than symbol with a backslash. Another 10 points!

 

Mark_57945
Nimbostratus
Nimbostratus

I just checked, and the is there if I try to edit the code on this page. DevCentral is probably blocking things that look like HTML tags to prevent HTML injection. I wonder how I fix that. I can't very well leave it broken.

 

Ah, that's how. By replacing the < and > with HTML entity strings in the code. sigh

 

WeaverJK
Nimbostratus
Nimbostratus

Mark - the rest of my post may help. Preface the less-than symbol with a backslash.

 

No worries. I figured the code was there, just not visible.

 

WeaverJK
Nimbostratus
Nimbostratus

Thanks again for your assistance, Mark.

 

Ryan77777
Altocumulus
Altocumulus

I ain't got time for a Perl module.

Hacked together from the above and Perl Monks. Seems to work!

!/usr/bin/perl

use IO::Socket;

my $programname = '/' . $0;
$programname =~ m/^.*\/([^\/]+)$/;
$programname = $1;
 
if ($programname eq '') {
    die "Bad data in program name\n"
}

my $pidfile = "/var/run/$programname.$node..$port.pid";
my $pid = "$$";

if (-f $pidfile ) {
    open(PID, "<$pidfile"); 
    my $pid = ; 
    close(PID);
    if ( $pid ) {
        chomp $pid; $pid =~ m/^(\d+)$/; $pid = $1;
        if ( $pid ) {
            kill 9, $pid;
        }
    }
    unlink($pidfile);
}

open(PID, ">$pidfile");
print PID $pid, "\n";
close(PID);

if (GetNTPTime($ARGV[0],$ARGV[1])) {
    print "OK\n";
} else {
    print "DOWN\n";
}

unlink($pidfile);
exit(0);

sub GetNTPTime{
    my $host = shift;
    my $timeout = 2;
    my $sock = IO::Socket::INET->new(
        Proto => 'udp',
        PeerPort => $ARGV[1],
        PeerAddr => $host,
      Timeout => $timeout
    )   or do { return 0 };
    my $ntp_msg = pack("B8 C3 N11", '00100011', (0)x14);
    $sock->send($ntp_msg) or do { return 0 };
    my $rin='';
    vec($rin, fileno($sock), 1) = 1;
    select(my $rout=$rin, undef, my $eout=$rin, $timeout) or do { return 0 };
    $sock->recv($ntp_msg, length($ntp_msg))
        or do { return 0 };
    my ($LIVNMode, $Stratum, $Poll, $Precision,
            $RootDelay, $RootDispersion, $RefIdentifier,
            $Reference, $ReferenceF, $Original, $OriginalF,
            $Receive, $ReceiveF, $Transmit, $TransmitF
        )   = unpack('a C2 c1 N8 N N B32', $ntp_msg);
    $Receive -= 2208988800;
    my $NTPTime = $Receive + $ReceiveF / 65536 / 65536;
    $RefIdentifier = unpack('A4', $RefIdentifier);
    my $LI = vec($LIVNMode, 3, 2);
    my $VN = unpack("C", $LIVNMode & "\x38") >> 3;
    my $Mode = unpack("C", $LIVNMode & "\x07");
    $sock->close;
    if ($RefIdentifier ne '1280') {
        return 1
    } else {
        return 0
    }
}
andymong
Nimbostratus
Nimbostratus

will this run on Big-IP 14 as well if I change the path to the lib?

Version history
Last update:
‎12-Mar-2015 15:23
Updated by:
Contributors