Commit 2faa886d authored by Mike Hibler's avatar Mike Hibler

Prototype lease-related sitevars and use them in create scripts.

Currently, these are not yet in the sitevariables table, I just hardwire
them in Lease.pm til we get the right set.
parent 5d92639e
......@@ -47,7 +47,84 @@ use Date::Parse;
use Data::Dumper;
use overload ('""' => 'Stringify');
my @LEASE_TYPES = ("stdataset","ltdataset");
#
# More DB fields we need:
#
# * last_check - a timestamp we update everytime we look at a lease.
# This way we can differentiate a lease that has truly been idle for
# a long time, from one that we just haven't checked in a long time
# (because the lease_daemon died or something).
#
# * renewals - if renewing a lease is going to be a first-class thing,
# we need to keep track of how many times it has been renewed (see
# the LEASE_VARS comments below.
#
my @LEASE_TYPES = ("stdataset", "ltdataset");
#
# Per-type Lease sitevars:
#
# maxsize Max size (MiB) of a dataset
# (0 == unlimited)
# maxlease Max time (days) from creation before lease is marked expired
# (0 == unlimited)
# maxidle Max time (days) from last use before lease is marked expired
# (0 == unlimited)
# graceperiod Time (days) before an expired dataset will be destroyed
# (0 == no grace period, unlimited makes no sense here)
# autodestroy If non-zero, destroy expired datasets after grace period
# otherwise lock them
# usequotas If non-zero, enforce per-project dataset quotas
# maxextend Number of times a user can extend the lease
# (0 == unlimited)
# extendperiod Length (days) of each user-requested extention
# (0 == do not allow extensions (when maxextend != 0))
#
my @LEASE_VARS = (
"maxsize",
"maxlease",
"maxidle",
"graceperiod",
"autodestroy",
"usequotas",
"maxextend",
"extendperiod"
);
#
# Plausible defaults:
#
# Short-term datasets. Allow large datasets but with short lease and grace
# periods. They are not quota-controlled and there is not an idle limit.
# Users can extend their leases by small amounts for a little while.
# After the grace period, these are automatically destroyed.
#
# Long-term datasets. Allow any-sized dataset that fits within the quota.
# These are generally expired based on idle time but may have a really long
# lease time as well. They are quota-controlled and users cannot extend
# their leases. After the grace period, these are are just marked as locked
# and unavailable.
#
my %LEASE_VAR_DEFAULTS = (
"stdataset/maxsize" => 1048576,# 1 TiB
"stdataset/maxlease" => 7, # 7 days
"stdataset/maxidle" => 0, # none
"stdataset/graceperiod" => 1, # 1 day
"stdataset/autodestroy" => 1, # yes
"stdataset/usequotas" => 0, # no
"stdataset/maxextend" => 2, # 2 user extentions
"stdataset/extendperiod" => 1, # 1 day per extention
"ltdataset/maxsize" => 0, # none
"ltdataset/maxlease" => 0, # none, use idle time
"ltdataset/maxidle" => 180, # 6 months
"ltdataset/graceperiod" => 180, # 6 months
"ltdataset/autodestroy" => 0, # no
"ltdataset/usequotas" => 1, # yes
"ltdataset/maxextend" => 1, # combined with...
"ltdataset/extendperiod" => 0, # ...means no user extention
);
#
# Lease states.
......@@ -253,7 +330,8 @@ sub Create($$;$) {
$lease_end = $argref->{'lease_end'};
$state = $argref->{'state'};
if (!($lease_id && $pid && $uid && $type && $lease_end && $state)) {
if (!($lease_id && $pid && $uid && $type &&
defined($lease_end) && $state)) {
print STDERR "Lease->Create: Missing required parameters in argref\n";
return undef;
}
......@@ -298,12 +376,11 @@ sub Create($$;$) {
return undef;
}
# XXX: minimum lease length should be changeable via sitevar.
if ($lease_end !~ /^\d+$/) {
print "Lease->Create: Invalid lease end time: $lease_end\n";
return undef;
}
if ($lease_end < time()) {
if ($lease_end && $lease_end < time()) {
print STDERR "Lease->Create: Lease end cannot be in the past\n";
return undef;
}
......@@ -313,16 +390,20 @@ sub Create($$;$) {
return undef;
}
#
# Get a unique lease index and slam this stuff into the DB.
# Note that lease_end == 0 means unlimited, for which we use the
# DB default value of 2037-01-19 03:14:07, one year before the
# (Unix) universe ends.
#
my $lease_idx = TBGetUniqueIndex('next_leaseidx');
DBQueryWarn("insert into project_leases set ".
"lease_idx=$lease_idx,".
"lease_id='$lease_id',".
"pid='". $pid->pid() ."',".
"owner_uid='". $uid->uid() ."',".
"type='$type',".
"lease_end=FROM_UNIXTIME($lease_end),".
($lease_end ? "lease_end=FROM_UNIXTIME($lease_end)," : "").
"state='$state',".
"statestamp=NOW(),".
"inception=NOW()")
......@@ -534,6 +615,46 @@ sub DeallocResources($) {
return 0;
}
#
# Return a hashref of sitevars for the indicated lease type.
# Why not let the caller make individual GetSiteVar calls? Well, efficiency
# mostly. There are a lot of em, and they are generally needed all together.
# By having them all fetched from here, I can also avoid putting these into
# the database until we decide for sure on the right set; i.e., by using
# LEASE_VAR_DEFAULTS.
#
sub SiteVars($$) {
my ($class, $ltype) = @_;
my %vars = ();
if (!grep {/^$ltype$/} @LEASE_TYPES) {
return undef;
}
my $hdr = "storage/$ltype";
my $query = DBQueryWarn("select name,value,defaultvalue ".
"from sitevariables where name like '$hdr/%%'");
return undef
if (!$query);
foreach my $var (@LEASE_VARS) {
$vars{$var} = undef;
# XXX tmp, til we settle on the set of vars
if (exists($LEASE_VAR_DEFAULTS{"$ltype/$var"})) {
$vars{$var} = $LEASE_VAR_DEFAULTS{"$ltype/$var"};
}
}
while (my ($var,$val,$defval) = $query->fetchrow_array()) {
if ($var =~ /^$hdr\/([-\w]+)$/ && exists($vars{$1})) {
$vars{$1} = defined($val) ? $val : $defval;
}
}
return \%vars;
}
#
# Return a list of all leases.
#
......@@ -790,7 +911,9 @@ sub AddTime($$) {
}
#
# Set a specific lease end time
# Set a specific lease end time.
# A value of zero means "unlimited" which is represented by a very large
# value in the DB (the default value).
#
sub SetEndTime($$) {
my ($self, $ntime) = @_;
......@@ -803,8 +926,15 @@ sub SetEndTime($$) {
return LEASE_ERROR_FAILED();
}
my $estr;
if ($ntime == 0) {
$estr = "DEFAULT(lease_end)";
} else {
$estr = "FROM_UNIXTIME($ntime)";
}
my $idx = $self->idx();
DBQueryWarn("update project_leases set lease_end=FROM_UNIXTIME($ntime) where lease_idx=$idx")
DBQueryWarn("update project_leases set lease_end=$estr where lease_idx=$idx")
or return LEASE_ERROR_FAILED();
$self->Refresh();
......@@ -831,7 +961,7 @@ sub ValidTransition($$) {
sub IsExpired($) {
my ($self) = @_;
if ($self->lease_end() < time()) {
if ($self->lease_end() <= time()) {
return 1;
}
......
......@@ -31,14 +31,14 @@ use Date::Parse;
#
sub usage()
{
print STDERR "Usage: createdataset [-hdU] [-o uid] [-a attrs] -s size -t type -e expiration name\n";
print STDERR "Usage: createdataset [-hdU] [-o uid] [-a attrs] [-t type] [-e expiration] -s size name\n";
print STDERR " -h This message\n";
print STDERR " -d Print additional debug info\n";
print STDERR " -U Create but do not approve; admin will need to approve later\n";
print STDERR " -s size Size in MiB\n";
print STDERR " -t type Type (stdataset or ltdataset)\n";
print STDERR " -t type Type ('stdataset' or 'ltdataset')\n";
print STDERR " -o uid Owner (defaults to caller)\n";
print STDERR " -e date Expiration date\n";
print STDERR " -e date Expiration date (or 'never')\n";
print STDERR " -a attrs comma-seperated string of key=value attributes\n";
print STDERR " name Name (in the form <pid>/<id>)\n";
exit(-1);
......@@ -48,7 +48,7 @@ my $debug = 0;
my $pid;
my $uid;
my $expire;
my $dstype;
my $dstype = "stdataset";
my $lname;
my $size;
my $approveme = 1;
......@@ -64,71 +64,6 @@ my %descrip = (
"ltdataset" => "long-term dataset"
);
#
# Sitevars:
#
# maxsize Max size (MiB) of a dataset
# (0 == unlimited)
# maxlease Max time (days) from creation before lease is marked expired
# (0 == unlimited)
# maxidle Max time (days) from last use before lease is marked expired
# (0 == unlimited)
# graceperiod Time (days) before an expired dataset will be destroyed
# (0 == no grace period, unlimited makes no sense here)
# autodestroy If non-zero, destroy expired datasets after grace period
# otherwise lock them
# usequotas If non-zero, enforce per-project dataset quotas
# maxextend Number of times a user can extend the lease
# (0 == unlimited)
# extendperiod Length (days) of each user-requested extention
# (0 == do not allow extensions (when maxextend != 0))
#
# Plausible defaults:
#
# Short-term datasets. Allow large datasets but with short lease and grace
# periods. They are not quota-controlled and there is not an idle limit.
# Users can extend their leases by small amounts for a little while.
# After the grace period, these are automatically destroyed.
#
# storage/stdataset/maxsize 1048576 # 1 TiB
# storage/stdataset/maxlease 7 # 7 days
# storage/stdataset/maxidle 0 # none
# storage/stdataset/graceperiod 1 # 1 day
# storage/stdataset/autodestroy 1 # yes
# storage/stdataset/usequotas 0 # no
# storage/stdataset/maxextend 2 # 2 user extentions
# storage/stdataset/extendperiod 1 # 1 day per extention
#
# Long-term datasets. Allow any-sized dataset that fits within the quota.
# These are generally expired based on idle time but have a really long
# lease time as well. They are quota-controlled and users cannot extend
# their leases. After the grace period, these are are just marked as locked
# and unavailable.
#
# storage/ltdataset/maxsize 0 # none, limited by project quota
# storage/ltdataset/maxlease 365 # 1 year
# storage/ltdataset/maxidle 30 # 1 month
# storage/ltdataset/graceperiod 30 # 1 month
# storage/ltdataset/autodestroy 0 # no
# storage/ltdataset/usequotas 1 # yes
# storage/ltdataset/maxextend 1 # combined with...
# storage/ltdataset/extendperiod 0 # ...means no user extention
#
# Maximum sizes
# XXX should come from sitevars
my %maxsize = (
"stdataset" => (10*1024*1024), # 10TiB
"ltdataset" => (1*1024*1024), # 1TiB
);
# Maximum length of a lease
# XXX should come from sitevars
my %maxlength = (
"stdataset" => (5*24*60*60), # 5 days
"ltdataset" => 0, # forever
);
# Protos
sub fatal($);
......@@ -188,17 +123,21 @@ if (defined($options{t})) {
$dstype = $options{t};
}
if (defined($options{e})) {
$expire = str2time($options{e});
if (!$expire) {
fatal("Could not parse expiration date.");
if ($options{e} eq "never") {
$expire = 0;
} else {
$expire = str2time($options{e});
if (!defined($expire)) {
fatal("Could not parse expiration date.");
}
}
}
if (defined($options{a})) {
$attrstr = $options{a};
}
if (!$size || !$dstype || !$expire || @ARGV != 1) {
print STDERR "Must specify size, type, expiration date, and name\n";
if (!$size || @ARGV != 1) {
print STDERR "Must specify size and name\n";
usage();
}
......@@ -260,15 +199,29 @@ if (!exists($descrip{$dstype})) {
}
#
# Check size: size must be > 0 and the size must be less than site limit.
# Fetch default values for the lease type. We use:
#
# maxsize Max size (MiB) of a dataset
# (0 == unlimited)
# maxlease Max time (days) from creation before lease is marked expired
# (0 == unlimited)
# usequotas If non-zero, enforce per-project dataset quotas
#
my $vars = Lease->SiteVars($dstype);
# convert to seconds
$vars->{"maxlease"} *= (24 * 60 * 60);
#
# Check size: size must be > 0 and the size must be less than site limit
# (if there is one).
#
if ($size <= 0) {
fatal("Size must be greater than zero.");
}
if ($approveme && $size > $maxsize{$dstype}) {
if ($approveme && $vars->{"maxsize"} > 0 && $size > $vars->{"maxsize"}) {
print STDERR
"Requested size ($size) is larger than allowed by default (" .
$maxsize{$dstype} . ").\n";
$vars->{"maxsize"} . ").\n";
print STDERR
"Try again with '-U' option and request special approval by testbed-ops.\n";
exit(1);
......@@ -276,20 +229,42 @@ if ($approveme && $size > $maxsize{$dstype}) {
#
# Check expiration: must be in the future and within the site-specific
# max lengths.
# max lengths. Note that an expire value of zero means "unlimited".
# If the user did not specify a value, we use the system max value.
#
my $now = time();
my $delta = $expire - $now;
if ($delta < 0) {
fatal("Expiration date is in the past!");
my $needapproval = 0;
if (!defined($expire)) {
# no user specified expiration, use default max (which may be unlimited)
if ($vars->{"maxlease"} > 0) {
$expire = $now + $vars->{"maxlease"};
} else {
$expire = 0;
}
} elsif ($expire == 0) {
# user specified unlimited, requires approval unless max is also unlimited
if ($vars->{"maxlease"} > 0) {
if ($approveme) {
print STDERR "Unlimited expiration not allowed by default.\n";
$needapproval = 1;
}
}
} else {
# user specified a date
if ($expire < $now) {
fatal("Expiration date is in the past!");
}
if ($approveme && ($expire - $now) > $vars->{"maxlease"}) {
my $mdate = localtime($now + $vars->{"maxlease"});
my $rdate = localtime($expire);
print STDERR "Expiration is beyond the maximum allowed by default ".
"($rdate > $mdate).\n";
$needapproval = 1;
}
}
if ($maxlength{$dstype} != 0 && $approveme && $delta > $maxlength{$dstype}) {
my $mdate = localtime($now + $maxlength{$dstype});
my $rdate = localtime($expire);
print STDERR
"Expiration ($rdate) is beyond the maximum allowed by default ($mdate).\n";
if ($needapproval) {
print STDERR
"Try again with '-U' option and request special approval by testbed-ops.\n";
"Try again with '-U' to request special approval by testbed-ops.\n";
exit(1);
}
......@@ -317,9 +292,10 @@ if (Lease->Lookup($pid, $lname)) {
}
#
# Check quota: long-term datasets count against the project quota.
# Check quota: if we are enforcing one, size + all existing leases
# must be below the project limit.
#
if ($dstype eq "ltdataset") {
if ($vars->{"usequotas"}) {
$quota = Quota->Lookup($pid, $qname);
if (!defined($quota)) {
fatal("No $qname quota associated with $pid.");
......@@ -389,9 +365,12 @@ if ($approveme) {
if ($lease->GotLock());
}
print "Created lease '$pid/$lname' for " . $descrip{$dstype} .
", expires on " . localtime($expire) . "\n";
print "Created lease '$pid/$lname' for " . $descrip{$dstype};
if ($expire == 0) {
print ", never expires.\n";
} else {
print ", expires on " . localtime($expire) . ".\n";
}
if (!$approveme) {
# send message to testbed-ops to approve?
print "NOTE: lease must still be approved before it can be used\n";
......
......@@ -27,18 +27,18 @@ use Getopt::Std;
use Date::Parse;
#
# Create a lease.
# Create a new lease.
#
sub usage()
{
print STDERR "Usage: createlease [-hd] [-o uid] [-a attrs] -t type -e expiration name\n";
print STDERR " -h This message\n";
print STDERR " -d Print additional debug info\n";
print STDERR " -t type Type of lease (stdataset or ltdataset)\n";
print STDERR " -o uid Owner of lease (defaults to caller)\n";
print STDERR " -e date Expiration date for lease\n";
print STDERR " -t type Type (must be specified)\n";
print STDERR " -o uid Owner (defaults to caller)\n";
print STDERR " -e date Expiration date (or 'never')\n";
print STDERR " -a attrs comma-seperated string of key=value attributes\n";
print STDERR " name Name of lease (of form <pid>/<id>)\n";
print STDERR " name Name (in the form <pid>/<id>)\n";
exit(-1);
}
my $optlist = "dho:t:e:a:";
......@@ -48,7 +48,7 @@ my $uid;
my $expire;
my $ltype;
my $lname;
my $attrstr;
my $attrstr = "";
my %attrs = ();
# Valid lease types
......@@ -84,6 +84,14 @@ $| = 1;
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:";
#
# XXX this is out of date right now, "deprecated" in favor of
# createdataset since those are the only recognized lease types right
# now. When we have other lease types, this should be updated and
# generalized to handle quotas, etc.
#
fatal("Use 'createdataset' to create dataset leases.");
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
......@@ -105,17 +113,21 @@ if (defined($options{t})) {
$ltype = $options{t};
}
if (defined($options{e})) {
$expire = str2time($options{e});
if (!$expire) {
fatal("Could not parse expiration date.");
if ($options{e} eq "never") {
$expire = 0;
} else {
$expire = str2time($options{e});
if (!defined($expire)) {
fatal("Could not parse expiration date.");
}
}
}
if (defined($options{a})) {
$attrstr = $options{a};
}
if (!$ltype || !$expire || @ARGV != 1) {
print STDERR "Must specify type, expiration date, and lease name\n";
if (!$ltype || @ARGV != 1) {
print STDERR "Must specify type and lease name\n";
usage();
}
......@@ -141,7 +153,7 @@ if ($uid) {
$user = User->Lookup($uid);
if (TBAdmin()) {
if (!defined($user)) {
fatal("No such user $uid\n");
fatal("No such user $uid");
}
} else {
if (!$user || !$user->SameUser($this_user)) {
......@@ -154,11 +166,11 @@ if ($uid) {
}
#
# Check project: user must be admin or have access to the project.
# Check project: caller must be admin or have local_root access in the project.
#
my $project = Project->Lookup($pid);
if (!defined($project)) {
fatal("No such project $pid\n");
fatal("No such project $pid");
}
if (!TBAdmin() &&
!$project->AccessCheck($this_user, TB_PROJECT_CREATELEASE())) {
......
......@@ -40,8 +40,8 @@ sub usage()
print STDERR " -U Unlock the lease (do not do this unless you know what you are doing!)\n";
print STDERR " -w time Try for up to time seconds to lock lease (0 means forever)\n";
print STDERR " -s state Update the state\n";
print STDERR " -e date Update the expiration date ('now' for current time)\n";
print STDERR " -l date Update the last used date ('now' for current time, '0' for never)\n";
print STDERR " -e date Update the expiration date ('now' for current time, 'never' for never)\n";
print STDERR " -l date Update the last used date ('now' for current time, 'never' for never)\n";
print STDERR " -a key=val Add or update attribute 'key' with value 'val'\n";
print STDERR " -r key Remove attribute 'key'\n";
......@@ -116,9 +116,11 @@ if (defined($options{s})) {
if (defined($options{e})) {
if ($options{e} eq "now") {
$expire = $now;
} elsif ($options{e} eq "never") {
$expire = 0;
} else {
$expire = str2time($options{e});
if (!$expire) {
if (!defined($expire)) {
fatal("Could not parse expiration date.");
}
}
......@@ -126,6 +128,8 @@ if (defined($options{e})) {
if (defined($options{l})) {
if ($options{l} eq "now") {
$lastused = $now;
} elsif ($options{l} eq "never") {
$lastused = 0;
} else {
$lastused = str2time($options{l});
if (!defined($lastused)) {
......@@ -152,7 +156,8 @@ if (defined($options{w})) {
}
}
if (!($state || $expire || defined($lastused) || $addattr || $delattr || $dounlock)) {
if (!($state || defined($expire) || defined($lastused) ||
$addattr || $delattr || $dounlock)) {
print STDERR "Must specify SOME action!\n";
usage();
}
......@@ -191,7 +196,7 @@ if (!defined($project)) {
#
# Check dates: must be appropriately in the past/future.
#
if ($expire && $expire < $now) {
if (defined($expire) && $expire != 0 && $expire < $now) {
fatal("Cannot set expiration date in the past.");
}
if (defined($lastused) && $lastused > $now) {
......@@ -260,7 +265,7 @@ else {
fatal("$pid/$lname: lease state changed ".
"while waiting for the lock ($ostate => $nstate).");
}
if ($expire && ($oexpire != $nexpire)) {
if (defined($expire) && ($oexpire != $nexpire)) {
fatal("$pid/$lname: lease expiration changed ".
"while waiting for the lock ($oexpire => $nexpire).");
}
......@@ -336,15 +341,19 @@ if ($state) {
}
# Handle expiration date
if ($expire) {
if (defined($expire)) {
# XXX in case time ticked on us after we got "now"
$expire = time()
if ($expire == $now);
if ($lease->SetEndTime($expire)) {
fatal("$pid/$lname: could not update expiration time.");
} else {
my $t = localtime($expire);
print "$pid/$lname: set expiration date to '$t'.\n";
if ($expire == 0) {
print "$pid/$lname: set to no expiration date.\n";
} else {
my $t = localtime($expire);
print "$pid/$lname: set expiration date to '$t'.\n";
}
}
}
......
......@@ -2755,7 +2755,7 @@ class createdataset:
print " -U - Create but do not approve dataset";
print " -s size - Size in MiB (1 MiB == 1024*1024)";
print " -t type - Type: one of 'stdataset' (default) or 'ltdataset'";
print " -e expire - Expiration date";
print " -e expire - Expiration date ('0' == never, default is system defined)";
print "";
print "Create a dataset with the indicated size and name";
wrapperoptions();
......
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