Commit 294fade1 authored by Gary Wong's avatar Gary Wong

Add future reservations and admission control.

Right now this is strictly advisory.  In particular, swap-ins go through
the normal path and are NOT forced to comply with admission control
wrt future reservations; therefore, reservations don't yet come with
any guarantees at all.
parent 172386d1
......@@ -1264,7 +1264,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
db/update_permissions \
db/grabron db/stategraph db/readycount \
db/idletimes db/idlemail db/xmlconvert \
db/libdb.py db/elabinelab_bossinit \
db/libdb.py db/elabinelab_bossinit db/Reservation.pm \
ipod/GNUmakefile \
os/GNUmakefile os/split-image.sh \
pxe/GNUmakefile pxe/bootinfo.restart \
......@@ -1364,7 +1364,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
utils/mkblob utils/rmblob utils/ctrladdr utils/tcppd \
utils/mktestbedtest utils/pxelinux_makeconf \
utils/addvpubaddr utils/attend utils/atten utils/addrfdevice \
utils/addrfpath \
utils/addrfpath utils/reserve \
www/GNUmakefile www/defs.php3 www/dbdefs.php3 www/xmlrpc.php3 \
www/xmlrpcpipe.php3 \
www/swish.conf www/websearch \
......
......@@ -50,7 +50,8 @@ LIB_SCRIPTS = libdb.pm Node.pm libdb.py libadminctrl.pm Experiment.pm \
libEmulab.pm EmulabConstants.pm TraceUse.pm \
EmulabFeatures.pm Port.pm BlockstoreType.pm Blockstore.pm \
IPBuddyAlloc.pm IPBuddyWrapper.pm Lease.pm Quota.pm \
libTaintStates.pm WebSession.pm WebTask.pm Brand.pm
libTaintStates.pm WebSession.pm WebTask.pm Brand.pm \
Reservation.pm
# Stuff installed on plastic.
USERSBINS = genelists.proxy dumperrorlog.proxy backup
......
This diff is collapsed.
......@@ -56,7 +56,7 @@ SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl \
mktestbedtest fixrootcert addservers poolmonitor \
node_exclude managetaint shutdown-shared imagerelease \
runsonxen pxelinux_makeconf attend atten \
addrfdevice addrfpath
addrfdevice addrfpath reserve
WEB_SBIN_SCRIPTS= webnewnode webdeletenode webspewconlog webarchive_list \
webwanodecheckin webspewimage webdumpdescriptor webemulabfeature \
......
#!/usr/bin/perl -w
#
# Copyright (c) 2016 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;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
use libtestbed;
use Project;
use Reservation;
sub usage()
{
print STDERR "Usage: reserve [-c] [-f] [-n] -t type [-s start] [-e end] " .
"pid count\n";
print STDERR " reserve -i pid\n";
print STDERR " reserve -l\n";
print STDERR " -h This message\n";
print STDERR " -c Clear existing reservation for project\n";
print STDERR " -f Force reservation into schedule, even if " .
"overcommitted\n";
print STDERR " -n Check feasibility only; don't actually reserve\n";
print STDERR " -t Node type\n";
print STDERR " -i Show existing reservation for project\n";
print STDERR " -l List all existing reservations\n";
print STDERR " -s Start time when reservation begins\n";
print STDERR " -e End time when reservation expires\n";
exit( 1 );
}
sub fatal($)
{
my ($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}
my $optlist = "hdcfnt:ile:s:";
my $debug = 0;
my $info = 0;
my $list = 0;
my $clear = 0;
my $force = 0;
my $impotent = 0;
my $starttime = time; # default to starting immediately
my $endtime = time + 24 * 60 * 60; # default to ending tomorrow
my $type;
my $pid;
my $count;
my $project;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:";
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{h})) {
usage();
}
if (defined($options{c})) {
$clear = 1;
}
if (defined($options{d})) {
$debug = 1;
}
if (defined($options{f})) {
$force = 1;
}
if (defined($options{n})) {
$impotent = 1;
}
if (defined($options{t})) {
$type = $options{t};
unless( $type =~ /^[-\w]+$/ ) {
fatal( "Invalid node type." );
}
}
if (defined($options{i})) {
$info = 1;
}
if (defined($options{l})) {
$list = 1;
}
if (defined($options{"e"})) {
$endtime = str2time( $options{"e"} );
if( !defined( $endtime ) ) {
fatal("Could not parse -e option.");
}
}
if (defined($options{"s"})) {
$starttime = str2time( $options{"s"} );
if( !defined( $starttime ) ) {
fatal("Could not parse -s option.");
}
}
if ($info) {
usage() if( @ARGV != 1 );
$pid = $ARGV[0];
}
elsif ($list) {
usage() if(@ARGV);
}
else {
usage() if( @ARGV < 2 || !defined( $type ) );
$pid = shift(@ARGV);
$count = shift(@ARGV);
if( $count < 1 ) {
fatal( "Must reserve at least one node." );
}
if( $endtime <= $starttime ) {
fatal( "Reservation must not end until after it starts." );
}
if( $endtime <= time ) {
fatal( "Reservation end time has already passed." );
}
if( $endtime > time + 3 * 365 * 24 * 60 * 60 ) {
fatal( "Reservation ends too far in the future." );
}
}
#
# List all pending reservations.
#
if ($list) {
my $query = $type ? "SELECT pid, nodes, type, start, end FROM " .
"future_reservations WHERE type='$type' ORDER BY start" :
"SELECT pid, nodes, type, start, end FROM future_reservations " .
"ORDER BY start";
my $query_result = DBQueryFatal( $query );
if( $query_result->numrows ) {
print "Start End Project Nodes Type\n";
print "----- --- ------- ----- ----\n";
}
while( my $row = $query_result->fetchrow_hashref() ) {
my $pid = $row->{'pid'};
my $nodes = $row->{'nodes'};
my $type = $row->{'type'};
my $start = $row->{'start'};
my $end = $row->{'end'};
printf( "%20s %20s %-20s %5d %s\n", $start, $end, $pid, $nodes, $type );
}
exit(0);
}
if ($pid =~ /^(.*):(.*)$/) {
require GeniHRN;
my $urn = GeniHRN::Generate($pid, "authority", "sa");
$project = Project->LookupNonLocal($urn);
if (!defined($project)) {
fatal("No such nonlocal project $pid\n");
}
$pid = $project->pid();
}
else {
$project = Project->Lookup($pid);
if (!defined($project)) {
fatal("No such project $pid\n");
}
}
my $pid_idx = $project->pid_idx();
#
# Show and exit.
#
if ($info) {
my $query = $type ? "SELECT uid, nodes, type, start, end FROM " .
"future_reservations WHERE type='$type' AND pid_idx=$pid_idx " .
"ORDER BY start" : "SELECT uid, nodes, type, start, end FROM " .
"future_reservations WHERE pid_idx=$pid_idx ORDER BY start";
my $query_result = DBQueryFatal( $query );
if( $query_result->numrows ) {
print "Start End User Nodes Type\n";
print "----- --- ---- ----- ----\n";
}
while( my $row = $query_result->fetchrow_hashref() ) {
my $uid = $row->{'uid'};
my $nodes = $row->{'nodes'};
my $type = $row->{'type'};
my $start = $row->{'start'};
my $end = $row->{'end'};
printf( "%19s %19s %-19s %5d %s\n", $start, $end, $uid, $nodes, $type );
}
exit(0);
}
#
# Verify user, must be admin or root.
#
my $this_user;
if ($UID) {
$this_user = User->ThisUser();
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
if (!$this_user->IsAdmin()) {
fatal("You are not a testbed administrator!");
}
}
#
# Clear and exit.
#
if ($clear) {
my $res = Reservation->Lookup( $pid, $starttime, $endtime, $type, $count );
if( !defined( $res ) ) {
print STDERR "reserve: no matching reservation found.\n";
exit( 1 );
}
$res->Cancel();
exit( 0 );
}
#
# Do not allow this as root; we want proper history.
#
if ($UID == 0) {
fatal("Please do not run this as root!");
}
my $uid = $this_user->uid();
my $uid_idx = $this_user->uid_idx();
my $res = Reservation->Create( $pid, $uid, $starttime, $endtime, $type,
$count );
print "$res\n" if( $debug );
while( 1 ) {
my $version = Reservation->GetVersion();
my $reservations = Reservation->LookupAll( $type );
push( @$reservations, $res );
my $error;
if( !Reservation->IsFeasible( $reservations, \$error ) ) {
print STDERR "reserve: $error\n";
if( $force ) {
print STDERR "Continuing anyway!\n";
} else {
exit( 1 );
}
}
exit( 0 ) if( $impotent );
next if( !defined( Reservation->BeginTransaction( $version ) ) );
$res->Book();
Reservation->EndTransaction();
last;
}
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