aboutsummaryrefslogblamecommitdiff
path: root/check_speedtest_servers
blob: 8e74510c298a26fe65fd6c549cd67d62ead7a025 (plain) (tree)
1
2
3
4
5
6
7
8
9
                   







                                                                             
             
















                                    
                      




















































                                                                              

                                         





                                                                           
















































                                                                                
#!/usr/bin/env perl

#
# Check an Ookla Speedtest server with a specified URL and/or host is present
# on the list of servers.
#
# Author: Tom Ryder <tom@sanctum.geek.nz>
# Copyright: 2018 Tom Ryder
#
package main;

# Force me to write this properly
use strict;
use warnings;
use utf8;

# Require at least this Perl version
# Should work even on very old Perls
use 5.006;

# Import required modules
use English '-no_match_vars';
use LWP::UserAgent ();
use Monitoring::Plugin qw(%ERRORS);
use XML::LibXML;

# Decree package version
our $VERSION = '0.06';

# Add description and license package variables
our $DESCRIPTION = <<'EOF';
This plugin retrieves the list of speedtest servers from speedtest.net and
checks for the presence of at least one server with the given host and/or URL.
EOF
our $LICENSE = <<'EOF';
This plugin is distributed under an MIT license. See LICENSE, or visit
<https://opensource.org/licenses/MIT>. Thanks to Inspire Net Ltd for allowing
this open-source fork.
EOF

# Define custom options
our @OPTS = (
    {
        spec  => 'host|h=s',
        label => 'HOSTNAME:PORT',
        help  => 'Hostname:port pair to find in list, usually *:8080',
    },
    {
        spec  => 'url|u=s',
        label => 'URL',
        help  => 'URL to find in list, usually ends in /upload.php',
    },
);

# URL from which the server list should be retrieved
our $SERVERS_LIST_URL = 'https://www.speedtest.net/speedtest-servers.php';

# Build Monitoring::Plugin object
my $mp = Monitoring::Plugin->new(
    usage   => 'Usage: %s --host|-H HOSTNAME:PORT --url|-u URL',
    version => $VERSION,
    blurb   => $DESCRIPTION,
    license => $LICENSE,
) or die "Failed plugin construct\n";

# Anything that dies in here will raise ->plugin_die()
eval {

    # Define and parse custom options
    for my $opt (@OPTS) {
        $mp->add_arg( %{$opt} );
    }
    $mp->getopts();

    # At least one of --host and --url must be specified
    length $mp->opts->host
      or length $mp->opts->url
      or die "One or both --host or --url must be specified\n";

    # Build a user agent that accepts only XML
    my $ua = LWP::UserAgent->new();

    # Attempt to retrieve the server list
    my $headers = HTTP::Headers->new(
        'Accept'          => 'application/xml;text/xml',
        'Accept-Encoding' => scalar HTTP::Message::decodable(),
    );
    my $request = HTTP::Request->new( 'GET', $SERVERS_LIST_URL, $headers );
    my $response = $ua->request($request);
    $response->is_success
      or die "$response->status_line\n";

    # Parse the server list as an XML document
    my $lxml = XML::LibXML->new();
    my $doc = $lxml->load_xml( string => $response->decoded_content )
      or die "Failed to parse response XML\n";

    # Build an XPath query object
    my $xpc = XML::LibXML::XPathContext->new($doc)
      or die "Failed to build XPath query object on response XML\n";

    # Build a query depending on which options we were provided
    ## no critic (RequireInterpolationOfMetachars)
    my $query = '/settings/servers/server';
    if ( $mp->opts->url ) {
        $query .= sprintf q{[@url='%s']}, $mp->opts->url;
    }
    if ( $mp->opts->host ) {
        $query .= sprintf q{[@host='%s']}, $mp->opts->host;
    }

    # Check that we have at least one matching server
    my @servers = $xpc->findnodes($query);
    my $code    = $mp->check_threshold(
        check    => scalar @servers,
        critical => '1:',
    );
    my $message = sprintf '%u matching servers', scalar @servers;
    $mp->add_message( $code, $message );

    # Add OK-level messages showing the conditions we applied
    # If we find an actual problem, "OK" will get replaced by ->check_messages()
    if ( $mp->opts->host ) {
        $mp->add_message( $ERRORS{OK}, sprintf 'host=%s', $mp->opts->host );
    }
    if ( $mp->opts->url ) {
        $mp->add_message( $ERRORS{OK}, sprintf 'url=%s', $mp->opts->url );
    }

    # Exit with determined code and messages
    $mp->plugin_exit(
        $mp->check_messages(
            join     => q{, },
            join_all => q{, },
        ),
    );

} or $mp->plugin_die($EVAL_ERROR);