Commit b3da5798 authored by Leigh Stoller's avatar Leigh Stoller

Add search IP button to current experiment list and history list.

This is actually implemented in a backend perl script, so you can
do the seach from the command line, but that would be silly, right?
parent 736fed17
......@@ -34,7 +34,7 @@ BIN_SCRIPTS = manage_profile manage_instance manage_dataset \
create_instance rungenilib ns2rspec nsgenilib.py \
rspec2genilib ns2genilib manage_reservations manage_gitrepo \
manage_images rtecheck checkprofile manage_extensions \
create_slivers
create_slivers searchip
SBIN_SCRIPTS = apt_daemon aptevent_daemon portal_xmlrpc apt_checkup \
portal_monitor apt_scheduler
LIB_SCRIPTS = APT_Profile.pm APT_Instance.pm APT_Dataset.pm APT_Geni.pm \
......@@ -42,7 +42,7 @@ LIB_SCRIPTS = APT_Profile.pm APT_Instance.pm APT_Dataset.pm APT_Geni.pm \
WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \
webcreate_instance webrungenilib webns2rspec webns2genilib \
webrspec2genilib webmanage_reservations webmanage_gitrepo \
webmanage_images webrtecheck
webmanage_images webrtecheck websearchip
APACHEHOOKS = apt_gitrepo.hook
WEB_SBIN_SCRIPTS= webportal_xmlrpc
LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS)
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2018 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 strict;
use English;
use Getopt::Std;
use Date::Parse;
#
# Convert an NS file into rspec using geni-lib and some lxml parsing.
#
sub usage()
{
print STDERR "Usage: searchip [-h [-s min] [-e max]] ipv4\n";
print STDERR "Options:\n";
print STDERR " -h - Search historical instances instead of current.\n";
print STDERR " -s - Start search timestamp for -h option.\n";
print STDERR " -e - End search timestamp for -h option.\n";
exit(-1);
}
my $optlist = "dt:hs:e:";
my $debug = 0;
my $historical = 0;
my $webtask_id;
my $webtask;
my $start;
my $end;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
# Protos
sub fatal($);
sub SearchCurrent();
sub SearchManifest($);
#
# Turn off line buffering on output
#
$| = 1;
# Locals
my $found;
#
# Untaint the path
#
$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/sbin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
use WebTask;
use APT_Instance;
use APT_Aggregate;
use GeniXML;
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"h"})) {
$historical = 1;
}
if (defined($options{"s"})) {
$start = $options{"s"};
if ($start !~ /^\d+$/) {
$start = str2time($start);
if (!$start) {
fatal("Invalid start time");
}
}
}
if (defined($options{"e"})) {
$end = $options{"e"};
if ($end !~ /^\d+$/) {
$end = str2time($end);
if (!$end) {
fatal("Invalid end time");
}
}
}
if (defined($options{"t"})) {
$webtask_id = $options{"t"};
$webtask = WebTask->Lookup($webtask_id);
if (!defined($webtask)) {
fatal("Could not lookup webtask");
}
$webtask->AutoStore(1);
}
usage()
if (!@ARGV);
my $IP = shift(@ARGV);
if ($IP !~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
fatal("Invalid IP format");
}
sub SearchCurrent()
{
my $found;
#
# Look up all instances and get the manifest.
#
my $query_result =
DBQueryFatal("select uuid from apt_instances");
while (my ($uuid) = $query_result->fetchrow_array()) {
my $instance = APT_Instance->Lookup($uuid);
next
if (!defined($instance));
foreach my $sliver ($instance->AggregateList()) {
next
if (!$sliver->manifest());
my $manifest = GeniXML::Parse($sliver->manifest());
if (! defined($manifest)) {
print STDERR "Could not parse manifest for $sliver\n";
next;
}
if (SearchManifest($manifest)) {
$found = $sliver;
last;
}
}
last
if ($found);
}
if (!$found) {
print "No matching instance for IP\n";
if (defined($webtask)) {
$webtask->output("No matching instance for IP");
$webtask->Exited(1);
}
exit(1);
}
my $instance = $found->instance();
my $pid = $instance->pid();
my $name = $instance->name();
my $aggregate_urn = $found->aggregate_urn();
my $aggregate = APT_Aggregate->Lookup($aggregate_urn);
if (!defined($aggregate)) {
fatal("Cannot lookup aggregate object for $aggregate_urn");
}
my $cluster_name = $aggregate->name();
my $mesg = "Found IP: $pid/$name - $cluster_name";
print $mesg . "\n";
if (defined($webtask)) {
$webtask->instance($instance->uuid);
$webtask->aggregate_urn($aggregate_urn);
$webtask->output($mesg);
$webtask->Exited(0);
}
exit(0);
}
sub SearchHistory()
{
my @result = ();
my %dups = ();
my $minmax = "";
if (defined($start)) {
$minmax = "(UNIX_TIMESTAMP(h.created) > $start) and ";
}
if (defined($end)) {
$minmax .= "(UNIX_TIMESTAMP(h.destroyed) < $end) and ";
}
my $query_result =
DBQueryFatal("select h.uuid,ah.aggregate_urn,ah.manifest from ".
" apt_instance_history as h ".
"left join apt_instance_aggregate_history as ah on ".
" ah.uuid=h.uuid ".
"where $minmax ah.manifest like '%${IP}%'");
while (my ($uuid,$aggregate_urn,$manifest) =
$query_result->fetchrow_array()) {
# Watch for dups.
if (exists($dups{$uuid})) {
next;
}
$dups{$uuid} = 1;
my $manifest = GeniXML::Parse($manifest);
if (! defined($manifest)) {
print STDERR "Could not parse manifest for $uuid,$aggregate_urn\n";
next;
}
if (SearchManifest($manifest)) {
my $history_result =
DBQueryFatal("select pid,name from apt_instance_history ".
"where uuid='$uuid'");
next
if (!$query_result->numrows);
my ($pid,$name) = $query_result->fetchrow_array();
my $aggregate = APT_Aggregate->Lookup($aggregate_urn);
if (!defined($aggregate)) {
fatal("Cannot lookup aggregate object for $aggregate_urn");
}
my $cluster_name = $aggregate->name();
my $mesg = "Found IP: $pid/$name - $cluster_name - $uuid";
print $mesg . "\n";
push(@result, $uuid);
}
}
if (defined($webtask)) {
$webtask->instancelist(\@result);
$webtask->Exited(0);
}
exit(0);
}
sub SearchManifest($)
{
my ($manifest) = @_;
#
# Check the routable pool bindings first
#
my $pools = GeniXML::GetAddressPools($manifest);
foreach my $pool (@{$pools}) {
foreach my $ipref (@{$pool->{'list'}}) {
if ($ipref->{'ipv4'} eq $IP) {
return 1;
}
}
}
foreach my $node (GeniXML::FindNodes("n:node",
$manifest)->get_nodelist(),
GeniXML::FindNodesNS("n:vhost",
$manifest,
$GeniXML::EMULAB_NS)->get_nodelist()) {
my $host = FindFirst("n:host", $node);
next
if (!defined($host));
my $ipv4 = GetText("ipv4", $host);
next
if (!defined($ipv4));
if ($ipv4 eq $IP) {
return 1;
}
}
return 0;
}
if ($historical) {
SearchHistory();
}
else {
SearchCurrent();
}
exit(0);
sub fatal($) {
my ($mesg) = $_[0];
if (defined($webtask)) {
$webtask->output($mesg);
$webtask->Exited(-1);
}
print STDERR "*** $0:\n".
" $mesg\n";
exit(-1);
}
<?php
#
# Copyright (c) 2000-2018 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/>.
#
# }}}
#
chdir("..");
include_once("webtask.php");
chdir("apt");
include_once("profile_defs.php");
include_once("instance_defs.php");
include_once("aggregate_defs.php");
$portals = array();
if ($TBMAINSITE) {
$portals["www.aptlab.net"] = "APT";
$portals["www.cloudlab.us"] = "Cloud";
$portals["www.emulab.net"] = "Emulab";
$portals["www.phantomnet.org"] = "Phantom";
$portals["www.powderwireless.net"] = "Powder";
}
else {
$portals[$APTHOST] = "Emulab";
}
function Do_Search()
{
global $this_user, $urn_mapping;
global $ajax_args, $portals, $APTHOST;
$isadmin = (ISADMIN() || ISFOREIGN_ADMIN() ? 1 : 0);
$results = array();
if (!$isadmin) {
if (isset($ajax_args["target_user"])) {
$target_user = User::LookupByUid(ajax_args["target_user"]);
if (!$target_user) {
SPITAJAX_ERROR(1, "No such user");
return;
}
if (!$target_user->SameUser($this_user)) {
SPITAJAX_ERROR(1, "Not enough permission to view info!");
return;
}
}
elseif (isset($ajax_args["target_project"])) {
$target_project = Project::LookupByPid(ajax_args["target_project"]);
if (!$target_project) {
SPITAJAX_ERROR(1, "No such project");
return;
}
$approved = 0;
if (!$target_project->IsMember($this_user, $approved) &&
$approved) {
SPITAJAX_ERROR(1, "Not enough permission to view this info!");
return;
}
}
}
$whereclause = "";
if (isset($target_project)) {
$target_idx = $target_project->pid_idx();
$whereclause = "where h.pid_idx='$target_idx'";
}
elseif (isset($target_user)) {
$target_idx = $target_user->idx();
$whereclause = "where h.creator_idx='$target_idx'";
}
if (isset($ajax_args["portalonly"])) {
if ($whereclause != "") {
$whereclause = "$whereclause and ";
}
else {
$whereclause = "where ";
}
$whereclause .= "servername='$APTHOST' ";
}
if (!(isset($ajax_args["min"]) && isset($ajax_args["max"]))) {
SPITAJAX_ERROR(1, "Must provide min and max");
return;
}
$min = $ajax_args["min"];
$max = $ajax_args["max"];
if (!is_numeric($min) || $min < 0) {
SPITAJAX_ERROR(1, "Bad value for min");
return;
}
if (!is_numeric($max) || $max < 0 || $max < $min) {
SPITAJAX_ERROR(1, "Bad value for max");
return;
}
# Hand off.
if ($isadmin && isset($ajax_args["IP"])) {
return SearchForIP($ajax_args["IP"], $min, $max);
}
if ($whereclause != "") {
$whereclause = "$whereclause and ";
}
else {
$whereclause = "where ";
}
if (isset($min)) {
$whereclause .= "UNIX_TIMESTAMP(h.started) > $min ";
if (isset($max)) {
$whereclause .= "and ";
}
}
if (isset($max)) {
$whereclause .= "UNIX_TIMESTAMP(h.started) < $max ";
}
$query_result =
DBQueryFatal("select h.uuid,h.profile_version,h.created, ".
" h.started,h.destroyed,h.servername, ".
" h.creator,p.uuid as profile_uuid,h.pid,u.email, ".
" h.physnode_count,h.virtnode_count,".
" h.name as instance_name,p.name as profile_name, ".
" truncate(h.physnode_count * ".
" ((UNIX_TIMESTAMP(h.destroyed) - ".
" UNIX_TIMESTAMP(h.started)) / 3600.0),2) as phours, ".
" GROUP_CONCAT(aa.abbreviation) as clusters ".
" from apt_instance_history as h ".
"left join apt_instance_aggregate_history as ia ".
" on ia.uuid=h.uuid ".
"left join apt_aggregates as aa on aa.urn=ia.aggregate_urn ".
"left join apt_profile_versions as p on ".
" p.profileid=h.profile_id and ".
" p.version=h.profile_version ".
"left join geni.geni_users as u on u.uuid=h.creator_uuid ".
$whereclause . " " .
"group by h.uuid order by h.started desc");
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$pname = $row["profile_name"];
$iname = $row["instance_name"];
$pproj = $row["pid"];
$puuid = $row["profile_uuid"];
$created = DateStringGMT($row["created"]);
$started = DateStringGMT($row["started"]);
$destroyed = DateStringGMT($row["destroyed"]);
$creator = $row["creator"];
$email = $row["email"];
$pcount = $row["physnode_count"];
$vcount = $row["virtnode_count"];
$phours = $row["phours"];
$portal = $portals[$row["servername"]];
$clusters = $row["clusters"];
# Backwards compat.
if (!isset($pproj)) {
$pproj = "";
}
if (!isset($destroyed)) {
$destroyed = "";
}
if (!isset($iname)) {
$iname = "&nbsp;";
}
# If a guest user, use email instead.
if (isset($email)) {
$creator = $email;
}
# Save space with array instead of hash.
$instance =
array($pname, $pproj, $puuid, $pcount, $vcount,
$creator, $started, $destroyed, $phours, $iname,
$uuid, $created, $portal, $clusters);
$results[] = $instance;
}
SPITAJAX_RESPONSE($results);
}
function SearchForIP($ip, $min, $max)
{
global $this_user, $portals;
$this_uid = $this_user->uid();
$results = array();
if (!preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/", $ip)) {
SPITAJAX_ERROR(1, "Invalid IP");
return;
}
$webtask = WebTask::CreateAnonymous();
$retval = SUEXEC($this_uid, "nobody",
"websearchip -t " . $webtask->task_id() .
" -s $min -e $max -h $ip",
SUEXEC_ACTION_IGNORE);
$webtask->Refresh();
if ($retval != 0) {
if (!$webtask->exited() || $retval < 0) {
SUEXECERROR(SUEXEC_ACTION_CONTINUE);
SPITAJAX_ERROR(-1, "Internal error");
}
else {
SPITAJAX_ERROR($webtask->exitcode(), $webtask->output());
}
$webtask->Delete();
return;
}
$instances = $webtask->TaskValue("instancelist");
$webtask->Delete();
foreach ($instances as $uuid) {
$instance = InstanceHistory::Lookup($uuid);
if (!$instance) {
continue;
}
$iname = $instance->name();
$pproj = $instance->pid();
$created = DateStringGMT($instance->created());
$started = DateStringGMT($instance->started());
$destroyed = DateStringGMT($instance->destroyed());
$creator = $instance->creator();
$pcount = $instance->physnode_count();
$vcount = $instance->virtnode_count();
$portal = $portals[$instance->servername()];
$phours = round(pcount *
((strtotime($instance->destroyed()) -
strtotime($instance->created())) / 3600.0));
# Profile Info.
$profile = Profile::Lookup($instance->profile_id(),
$instance->profile_version());
if ($profile) {
$pname = $profile->name();
$puuid = $profile->uuid();
}
$clusters = array();
foreach ($instance->slivers() as $sliver) {
$aggregate = Aggregate::Lookup($sliver["aggregate_urn"]);
$clusters[] = $aggregate->abbreviation();
}
$clusters = join(",", $clusters);
# Backwards compat.
if (!isset($pproj)) {
$pproj = "";
}
if (!isset($iname)) {
$iname = "&nbsp;";
}
# Save space with array instead of hash.
$instance =
array($pname, $pproj, $puuid, $pcount, $vcount,
$creator, $started, $destroyed, $phours, $iname,
$uuid, $created, $portal, $clusters);
$results[] = $instance;
}
SPITAJAX_RESPONSE($results);
}
?>
......@@ -26,7 +26,7 @@ include("defs.php3");
chdir("apt");
include("quickvm_sup.php");
include("profile_defs.php");
$page_title = "My Profiles";
$page_title = "Activity";
#
# Verify page arguments.
......@@ -41,6 +41,7 @@ $optargs = OptionalPageArguments("target_user", PAGEARG_USER,
#
RedirectSecure();
$this_user = CheckLoginOrRedirect();
$isadmin = (ISADMIN() || ISFOREIGN_ADMIN() ? 1 : 0);
SPITHEADER(1);
if (!(ISADMIN() || ISFOREIGN_ADMIN())) {
......@@ -60,137 +61,30 @@ if (!(ISADMIN() || ISFOREIGN_ADMIN())) {
$target_user = $this_user;
}
}
$instances = array();
#
# Allow for targeted searches
#
$whereclause = "";
if (isset($target_user)) {
$target_idx = $target_user->idx();
$whereclause = "where h.creator_idx='$target_idx'";
}
elseif (isset($target_project)) {
$target_idx = $target_project->pid_idx();
$whereclause = "where h.pid_idx='$target_idx'";
}
if (isset($portalonly) && $portalonly) {
if ($whereclause != "") {
$whereclause = "$whereclause and ";
}
else {
$whereclause = "where ";
}
$whereclause .= "servername='$APTHOST' ";
$target_pid = $target_project->pid_idx();
}
# Lets default to last three months if neither min or max provided
# Lets default to last month if neither min or max provided
if (! (isset($min) || isset($max))) {
$min = time() - (90 * 3600 * 24);
}
if (isset($min) || isset($max)) {
if ($whereclause != "") {
$whereclause = "$whereclause and ";
}
else {
$whereclause = "where ";
}
if (isset($min)) {
$whereclause .= "UNIX_TIMESTAMP(h.started) > $min ";
if (isset($max)) {
$whereclause .= "and ";
}
}
if (isset($max)) {
$whereclause .= "UNIX_TIMESTAMP(h.started) < $max ";
}
}
$portals = array();
if ($TBMAINSITE) {
$portals["www.aptlab.net"] = "APT";
$portals["www.cloudlab.us"] = "Cloud";
$portals["www.emulab.net"] = "Emulab";
$portals["www.phantomnet.org"] = "Phantom";
$portals["www.powderwireless.net"] = "Powder";
}
else {
$portals[$APTHOST] = "Emulab";
}
$query_result =
DBQueryFatal("select h.uuid,h.profile_version,h.created, ".
" h.started,h.destroyed,h.servername, ".
" h.creator,p.uuid as profile_uuid,h.pid,u.email, ".
" h.physnode_count,h.virtnode_count,".
" h.name as instance_name,p.name as profile_name, ".
" truncate(h.physnode_count * ".
" ((UNIX_TIMESTAMP(h.destroyed) - ".
" UNIX_TIMESTAMP(h.started)) / 3600.0),2) as phours, ".
" GROUP_CONCAT(aa.abbreviation) as clusters ".
" from apt_instance_history as h ".
"left join apt_instance_aggregate_history as ia ".
" on ia.uuid=h.uuid ".
"left join apt_aggregates as aa on aa.urn=ia.aggregate_urn ".
"left join apt_profile_versions as p on ".
" p.profileid=h.profile_id and ".
" p.version=h.profile_version ".
"left join geni.geni_users as u on u.uuid=h.creator_uuid ".
$whereclause . " " .
"group by h.uuid order by h.started desc");
if (mysql_num_rows($query_result) == 0) {
$message = "<b>Oops, there is no activity to show you.</b><br>";
SPITUSERERROR($message);
exit();
}
if (1) {
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$pname = $row["profile_name"];
$iname = $row["instance_name"];
$pproj = $row["pid"];
$puuid = $row["profile_uuid"];
$created = DateStringGMT($row["created"]);
$started = DateStringGMT($row["started"]);
$destroyed = DateStringGMT($row["destroyed"]);
$creator = $row["creator"];
$email = $row["email"];
$pcount = $row["physnode_count"];
$vcount = $row["virtnode_count"];
$phours = $row["phours"];
$portal = $portals[$row["servername"]];
$clusters = $row["clusters"];
# Backwards compat.
if (!isset($pproj)) {
$pproj = "";
}
if (!isset($destroyed)) {
$destroyed = "";
}
if (!isset($iname)) {
$iname = "&nbsp;";
}
# If a guest user, use email instead.
if (isset($email)) {
$creator = $email;
}
# Save space with array instead of hash.
$instance =