Commit 58640e2c authored by Leigh Stoller's avatar Leigh Stoller

Export linktest via the CM API.

* Three actions are exported; start, stop, and status. The last is cause we
  have to poll to determine when linktest has actually finished or stopped.
  I hate all this polling.

* For start, linktest can be performed synchronously, which is fine on a
  small experiment, but in general you want to use the async option and
  check back later. When using async, we return the spewlog URL to the
  caller so that linktest can be monitored.

* A couple of minor changes to linktest itself for using a spew log.

* Some simple test rspecs and a linktest.py driver.
parent 8aeed2be
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -40,17 +40,19 @@ sub usage()
"-k - Kill a currently running linktest.\n".
"-t - Specify timeout in seconds.\n".
"-o - Specify output file for linktest results.\n".
"-e - Tell linktest to send error output to stdout or -o file\n".
"-m - Send email to swapper if linktest fails.\n".
"-r - Report results only, don't flag errors.\n".
"-d - Turn on debugging output.\n");
exit(-1);
}
my $optlist = "dkl:o:t:mfr";
my $optlist = "dkl:o:t:mfre";
my $debug = 1;
my $cancel = 0;
my $sendmail = 0;
my $forcerun = 0;
my $reportonly = 0;
my $noerrlog = 0;
my $timeout;
my $level;
my $output;
......@@ -143,6 +145,9 @@ if (defined($options{"o"})) {
die("Bad data in output file: $output\n");
}
}
if (defined($options{"e"})) {
$noerrlog = 1;
}
if (@ARGV != 2) {
usage();
}
......@@ -223,8 +228,8 @@ if (defined($linktest_pid) && $linktest_pid) {
}
}
elsif ($cancel) {
die("*** $0:\n".
" Linktest is not running on experiment $pid/$eid!\n")
print "Linktest is not running on experiment $pid/$eid!\n";
exit(0);
}
my @hosed = ();
......@@ -348,7 +353,8 @@ push(@cmdargs, ("-g", $unix_gidname));
push(@cmdargs, ("-p", $unix_pidname));
push(@cmdargs, ("-u", $dbuid));
push(@cmdargs, ("-e", "$pid/$eid"));
push(@cmdargs, ("-o", $errlog));
push(@cmdargs, ("-o", $errlog))
if (!$noerrlog);
push(@cmdargs, "-r")
if ($reportonly);
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2008-2015 University of Utah and the Flux Group.
# Copyright (c) 2008-2016 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
......@@ -106,6 +106,7 @@ my $DELETEIMAGE = "$TB/sbin/delete_image";
my $WAP = "$TB/sbin/withadminprivs";
my $SHAREVLAN = "$TB/sbin/sharevlan";
my $PANIC = "$TB/sbin/panic";
my $LINKTEST = "$TB/sbin/linktest_control";
my $XMLLINT = "/usr/local/bin/xmllint";
my $PRERENDER = "$TB/libexec/vis/prerender";
my $IMPORTER = "$TB/sbin/image_import";
......@@ -4798,5 +4799,160 @@ sub Panic($)
return 0;
}
#
# Another Emulab specific function to run linktest and return the results.
#
sub RunLinktest($)
{
my ($argref) = @_;
my $slice_urn = $argref->{'slice_urn'};
my $credentials = $argref->{'credentials'};
my $level = $argref->{'level'};
my $action = $argref->{'action'};
my $async = $argref->{'async'};
my $logfile;
if (! (defined($credentials) && defined($action) &&
defined($slice_urn))) {
return GeniResponse->MalformedArgsResponse("Missing arguments");
}
if ($action !~ /^(start|stop|status)$/) {
return GeniResponse->MalformedArgsResponse("Bad action: $action");
}
if ($action eq "start") {
$level = 1
if (!defined($level));
if ($level !~ /^\d$/ || $level < 1 || $level > 4) {
return GeniResponse->MalformedArgsResponse("Bad linktest level");
}
}
my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
return $credential
if (GeniResponse::IsResponse($credential));
my ($slice, $aggregate) = Credential2SliceAggregate($credential);
return $slice
if (defined($slice) && GeniResponse::IsResponse($slice));
if (! (defined($slice) && defined($aggregate))) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"Sliver does not exist");
}
my $user = GeniCM::CreateUserFromCertificate($credential);
return $user
if (GeniResponse::IsResponse($user));
main::AddLogfileMetaDataFromSlice($slice);
if ($slice_urn ne $slice->urn()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
"Credential does not match the URN");
}
if (!defined(GeniCM::FlipToUser($slice, $user))) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"FlipToUser failed");
}
my $experiment = $slice->GetExperiment();
if (!defined($experiment)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No local experiment for slice");
}
#
# When async, we are going to create a log file and return a spew
# url to the caller, who can then read the output of the linktest
# in real time.
#
if ($action eq "start" && defined($async)) {
$logfile = Logfile->Create($experiment->GetGroup());
if (!defined($logfile)) {
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
$logfile->SetPublic(1);
$logfile->Open();
my $mypid = main::WrapperFork();
if ($mypid) {
#
# Wait a few seconds for immediate errors or finish.
#
sleep(5);
my $kid = waitpid($mypid, &WNOHANG);
if ($kid == $mypid) {
my $stat = $?;
if ($stat & 127) {
# died with a signal, return the signal
$stat = $stat & 127;
} else {
# else return the exit code
$stat = $stat >> 8;
}
my $output = undef;
my $fname = $logfile->filename();
$output = `cat $fname` if (-s $fname);
$logfile->Delete(1);
print STDERR "foo\n";
if ($stat) {
return GeniResponse->Create(GENIRESPONSE_ERROR,
undef, $output);
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
{"status" => "stopped",
"results" => $output}, $output);
}
print STDERR $logfile->URL() . "\n";
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
{"status" => "running",
"url" => $logfile->URL()});
}
}
my $pid = $experiment->pid();
my $eid = $experiment->eid();
my $output;
if ($action eq "status") {
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
{"status" =>
($experiment->linktest_pid() ?
"running" : "stopped")});
}
elsif ($action eq "stop") {
$output = GeniUtil::ExecQuiet("$LINKTEST -k $pid $eid");
}
else {
my $command = "$LINKTEST -l $level -e ";
$command .= "-o " . $logfile->filename() . " "
if (defined($async));
$command .= "$pid $eid";
$output = GeniUtil::ExecQuiet($command);
}
my $exitval = $?;
if (defined($async)) {
if ($exitval) {
my $fname = $logfile->filename();
$output .= `cat $fname` if (-s $fname);
}
#
# Delay for bit then delete the log file. Need to do this a different
# way at some point.
#
$logfile->Close();
sleep(30);
$logfile->Delete(1);
}
if ($exitval) {
print STDERR $output;
return -1
if (defined($async));
return GeniResponse->Create(GENIRESPONSE_ERROR, undef, $output);
}
return 0
if (defined($async));
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
{"status" => "stopped",
"results" => $output}, $output);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
<?xml version="1.0" encoding="UTF-8"?>
<rspec xmlns="http://www.protogeni.net/resources/rspec/2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2
http://www.protogeni.net/resources/rspec/2/request.xsd"
type="request">
<node client_id="geni1"
exclusive="true">
<sliver_type name="raw-pc" />
<interface client_id="geni1:if0" />
</node>
<node client_id="geni2"
exclusive="true">
<sliver_type name="raw-pc" />
<interface client_id="geni2:if0" />
</node>
<node client_id="geni3"
exclusive="true">
<sliver_type name="raw-pc" />
<interface client_id="geni3:if0" />
</node>
<link client_id="lan0">
<interface_ref client_id="geni1:if0" />
<interface_ref client_id="geni2:if0" />
<interface_ref client_id="geni3:if0" />
<property source_id="geni1:if0" dest_id="lan0"
capacity="50000" latency="15" packet_loss="0.05"/>
<property source_id="lan0" dest_id="geni1:if0"
capacity="50000" latency="15" packet_loss="0.05"/>
<property source_id="geni2:if0" dest_id="lan0"
capacity="50000" latency="15" packet_loss="0.05"/>
<property source_id="lan0" dest_id="geni2:if0"
capacity="50000" latency="15" packet_loss="0.05"/>
<property source_id="geni3:if0" dest_id="lan0"
capacity="50000" latency="15" packet_loss="0.05"/>
<property source_id="lan0" dest_id="geni3:if0"
capacity="50000" latency="15" packet_loss="0.05"/>
</link>
</rspec>
......@@ -17,5 +17,9 @@ http://www.protogeni.net/resources/rspec/2/request.xsd"
<link client_id="link0">
<interface_ref client_id="geni1:if0" />
<interface_ref client_id="geni2:if0" />
<property source_id="geni1:if0" dest_id="geni2:if0"
capacity="50000" latency="15" packet_loss="0.05"/>
<property source_id="geni2:if0" dest_id="geni1:if0"
capacity="50000" latency="15" packet_loss="0.05"/>
</link>
</rspec>
#! /usr/bin/env python
#
# Copyright (c) 2008-2015 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
#
#
import sys
import pwd
import getopt
import os
import re
ACCEPTSLICENAME=1
dokill = 0;
action = "start"
execfile( "test-common.py" )
if len(REQARGS) == 1 and REQARGS[0] == "kill":
dokill = 1
action = "kill"
pass
#
# Get a credential for myself, that allows me to do things at the SA.
#
mycredential = get_self_credential()
print "Got my SA credential. Looking for slice ..."
#
# Lookup slice.
#
myslice = resolve_slice( SLICENAME, mycredential )
print "Found the slice, asking for a credential ..."
#
# Get the slice credential.
#
slicecred = get_slice_credential( myslice, mycredential )
print "Got the slice credential, " + action + "ing linktest ..."
#
# Start Linktest
#
params = {}
params["slice_urn"] = myslice["urn"]
params["credentials"] = (slicecred,)
if dokill:
params["action"] = "stop"
else:
params["action"] = "start"
params["level"] = 1
params["async"] = 1
pass
rval,response = do_method("cm", "RunLinktest", params, version="2.0")
if rval:
Fatal("Could not " + action + " Linktest")
pass
print str(response["value"])
......@@ -118,6 +118,7 @@ elsif ($GENI_VERSION eq "2.0") {
"AddNodes" => \&GeniCMV2::AddNodes,
"DeleteNodes" => \&GeniCMV2::DeleteNodes,
"Panic" => \&GeniCMV2::Panic,
"RunLinktest" => \&GeniCMV2::RunLinktest,
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment