All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit b3da5798 authored by Leigh B Stoller's avatar Leigh B 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) ||