diff --git a/account/getipinfo.in b/account/getipinfo.in new file mode 100644 index 0000000000000000000000000000000000000000..b90d8d1189adaa06ba7f8ccbf1d9f6713113f0e8 --- /dev/null +++ b/account/getipinfo.in @@ -0,0 +1,272 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2005-2022 University of Utah and the Flux Group. +# +# {{{EMULAB-LICENSE +# +# This file is part of the Emulab network testbed software. +# +# This file is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# This file is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public +# License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this file. If not, see <http://www.gnu.org/licenses/>. +# +# }}} +# +use English; +use strict; +use Getopt::Std; +use Data::Dumper; +use File::Temp qw(tempfile); +use JSON; + +# +# Ask https://ipinfo.io/ for IP info +# +# select cc.country,sum(t.count) as count from +# (select country,count(distinct(uid)) as count from login_history +# where IP is not null and location is not null and portal='cloudlab' +# group by country,uid) as t +# left join ccodes.ccodes as cc on cc.code=t.country +# group by cc.country order by count desc; +# +#select region,sum(t.count) as count from +# (select region,count(distinct(uid)) as count from login_history +# where IP is not null and location is not null and country='US' and +# portal='cloudlab' +# group by region,uid) as t +#group by region order by count desc; +# + +# +sub usage() +{ + print "Usage: getipinfo [-n]\n"; + print " getipinfo [-n] -p portal\n"; + exit(1); +} +my $optlist = "ndp:"; +my $impotent = 0; +my $debug = 0; +my $limit = 200; + +# +# Configure variables +# +my $TB = "@prefix@"; +my $token = "850749cc3b77dc"; +my $URL = "http://ipinfo.io/batch?token=${token}"; +my $CURL = "/usr/local/bin/curl"; + +# Load the Testbed support stuff. +use lib "@prefix@/lib"; +use emdb; +use User; +use emutil; + +# Protos +sub fatal($); +sub WriteResults($); + +# +# Turn off line buffering on output +# +$| = 1; + +my %options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (defined($options{"n"})) { + $impotent++; +} +if (defined($options{"d"})) { + $debug++; +} +if (defined($options{"p"})) { + my $portal = $options{"p"}; + if ($portal ne "cloudlab" && $portal ne "powder") { + fatal("Only cloudlab or powder portal please"); + } + exit(WriteResults($portal)); +} + +# +# Find unmatched IPs in the login_history table and batch them up +# for the request. +# +my $count = 0; + +while ($limit) { + $limit--; + + my $query_result = + DBQueryFatal("select distinct IP from login_history ". + "where location is null and IP is not null ". + "limit 100"); + + last + if ($query_result->numrows == 0); + + my %IPs = (); + + while (my ($IP) = $query_result->fetchrow_array()) { + $IPs{$IP} = $IP; + } + if (keys(%IPs)) { + # Create a temporary files for curl + my ($fpIn, $fnameIn) = tempfile("/tmp/iplistInXXXXX", UNLINK => 0); + if (!defined($fpIn)) { + fatal("Could not create temp file for IPs"); + } + my ($fpOut, $fnameOut) = tempfile("/tmp/iplistOutXXXXX", UNLINK => 0); + if (!defined($fpOut)) { + fatal("Could not create temp file for IPs"); + } + foreach my $IP (keys(%IPs)) { + print $fpIn "$IP\n"; + } + my $command = + "$CURL -s -o $fnameOut -XPOST --data-binary \@${fnameIn} $URL"; + if ($debug) { + print "$command\n"; + } + system($command); + if ($?) { + fatal("curl failure: '$command'\n"); + } + my $json = emutil::ReadFile($fnameOut); + if (!$json || $json eq "") { + fatal("No json received"); + } + my $results = eval { decode_json($json) }; + if ($@) { + fatal("Could not decode json data"); + } + if ($debug) { + print Dumper($results); + } + foreach my $IP (keys(%IPs)) { + my $ref = $results->{$IP}; + if (!defined($ref)) { + print STDERR "No data for $IP\n"; + next; + } + my $loc = $ref->{'loc'}; + my $country = $ref->{'country'}; + my $region = $ref->{'region'}; + + if (!defined($loc)) { + print STDERR "No data for $IP\n"; + + DBQueryFatal("update login_history set ". + " location='' ". + "where IP='$IP'"); + next; + } + $count++; + if ($impotent) { + print "Would set $IP: $loc,$country,$region\n"; + next; + } + else { + print "$IP: $loc,$country,$region\n"; + + DBQueryFatal("update login_history set ". + " location=" . DBQuoteSpecial($loc) . ", ". + " country=" . DBQuoteSpecial($country) . ", ". + " region=" . DBQuoteSpecial($region) . " ". + "where IP='$IP'"); + } + } + unlink($fnameIn); + unlink($fnameOut); + } + print "$count IPs completed\n"; + last + if (!$count); + + sleep(10); +} + +exit(0); + +# +# Write per portal results files. Queries take a while. +# +sub WriteResults($) +{ + my ($portal) = @_; + + print "These queries take time, get a cup of coffee.\n"; + + my $query_result = + DBQueryFatal("select cc.country,sum(t.count) as count from ". + " (select country,count(distinct(uid)) as count ". + " from login_history ". + " where IP is not null and location is not null and ". + " portal='$portal' ". + " group by country,uid) as t ". + "left join ccodes.ccodes as cc on cc.code=t.country ". + "group by cc.country order by count desc"); + + my $fname = "world-counts-${portal}.csv"; + print "Writing $fname ... \n"; + if (open(WORLD, ">$fname")) { + print WORLD "name,count\n"; + while (my ($country,$count) = $query_result->fetchrow_array()) { + next + if (!defined($country)); + + $country = "USA" + if ($country eq "United States"); + + print WORLD "$country,$count\n"; + } + close(WORLD); + } + else { + fatal("Could not open $fname for writing: $!\n"); + } + + $query_result = + DBQueryFatal("select region,sum(t.count) as count from ". + " (select region,count(distinct(uid)) as count ". + " from login_history ". + " where IP is not null and location is not null and ". + " country='US' and portal='$portal' ". + " group by region,uid) as t ". + "group by region order by count desc"); + + $fname = "us-counts-${portal}.csv"; + print "Writing $fname ... \n"; + if (open(STATES, ">$fname")) { + print STATES "name,count\n"; + while (my ($region,$count) = $query_result->fetchrow_array()) { + next + if (!defined($region)); + + print STATES "$region,$count\n"; + } + close(STATES); + } + else { + fatal("Could not open $fname for writing: $!\n"); + } + exit(0); +} + +sub fatal($) { + my($mesg) = $_[0]; + + die("*** $0:\n". + " $mesg\n"); +}